Я работаю над потоковым клиентом Twitter - после 1-2 дней постоянной работы я получаю использование памяти> 1,4 гигабайта (32-разрядный процесс), и вскоре после того, как он достигнет этого количества, я получу нехватку памяти. исключение для кода, который по сути такой (этот код выдаст ошибку через ‹30 секунд на моей машине):
while (true)
{
Task.Factory.StartNew(() =>
{
dynamic dyn2 = new ExpandoObject();
//get a ton of text, make the string random
//enough to be be interned, for the most part
dyn2.text = Get500kOfText() + Get500kOfText() + DateTime.Now.ToString() +
DateTime.Now.Millisecond.ToString();
});
}
Я профилировал это, и это определенно связано с тем, что класс находится в DLR (насколько я помню, у меня нет подробной информации здесь) xxRuntimeBinderxx и xxAggregatexx.
Этот ответ Эрика Липперта (Microsoft), кажется, указывает на то, что я m создание выражений, анализирующих объекты за кулисами, которые никогда не проходят сборщик мусора, даже если в моем коде нет ссылок на что-либо.
Если это так, есть ли в приведенном выше коде способ предотвратить или уменьшить его?
Мой запасной вариант - исключить динамическое использование, но я бы предпочел этого не делать.
Спасибо
Обновление:
14.12.12:
ОТВЕТ:
Способ заставить этот конкретный пример освободить свои задачи состоял в том, чтобы yield (Thread.Sleep(0)), который затем позволял GC обрабатывать освободившиеся задачи. Я предполагаю, что цикл сообщения/события не может быть обработан в этом конкретном случае.
В реальном коде, который я использовал (поток данных TPL), я не вызывал Complete() для блоков, потому что они должны были быть бесконечный поток данных - задача будет принимать сообщения Twitter до тех пор, пока Twitter их отправляет. В этой модели никогда не было причин сообщать каким-либо блокам, что они были выполнены, потому что они никогда не БЫЛИ выполнены, пока приложение работало.
К сожалению, не похоже, что блоки потока данных никогда не разрабатывались для очень долгой работы или обработки бесчисленного количества элементов, потому что они фактически хранят ссылку на все, что в них отправляется. Если я ошибаюсь, пожалуйста, дайте мне знать.
Таким образом, обходной путь заключается в том, чтобы периодически (в зависимости от использования вашей памяти - у меня было каждые 100 000 сообщений в Твиттере) освобождать блоки и настраивать их снова.
В соответствии с этой схемой потребление моей памяти никогда не превышает 80 мегабайт, а после повторного использования блоков и форсирования сборки мусора куча gen2 возвращается к 6 мегабайтам, и все снова в порядке.
17.10.12:
- "Это не делает ничего полезного": этот пример просто позволяет вам быстро сгенерировать проблему. Он состоит из нескольких сотен строк кода, которые не имеют никакого отношения к проблеме.
- «Бесконечный цикл, создающий задачу и, в свою очередь, создающий объекты»: помните — это просто быстро демонстрирует проблему — фактический код находится там, ожидая дополнительных потоковых данных. Кроме того, глядя на код, все объекты создаются внутри лямбды Action‹> в задаче. Почему это не очищается (в конце концов) после того, как оно выходит за рамки? Проблема также не из-за того, что это делается слишком быстро - фактическому коду требуется больше дня, чтобы достичь исключения нехватки памяти - это просто делает его достаточно быстрым, чтобы попробовать что-то.
- "Гарантировано ли освобождение задач?" Объект есть объект, не так ли? Насколько я понимаю, планировщик просто использует потоки в пуле, и лямбда-выражение, которое он выполняет, будет выброшено после того, как оно будет запущено, несмотря ни на что.