.Net 4 Утечки MemoryCache с одновременной сборкой мусора
Я использую новый MemoryCache в .Net 4 с максимальным ограничением размера кеша в MB (я тестировал он устанавливается между 10 и 200 МБ, в системах с 1,75 и 8 ГБ памяти). Я не устанавливаю время на основе объектов, поскольку я использую кеш-диск просто как высокопроизводительный диск, и пока есть место, я хочу, чтобы оно использовалось. К моему удивлению, кеш отказался выселять любые объекты, до тех пор, пока я не получу исключения SystemOutOfMemory
.
Я запустил perfmon, подключил мое приложение к .Net CLR Memory\#Bytes In All Heaps
, .Net Memory Cache 4.0
и Process\Private Bytes
- действительно, потребление памяти было из-под контроля, и никакие тайники для кэша не были зарегистрирован.
Был ли какой-то поиск в googling и stackoverflow, загружен и прикреплен CLRProfiler, и wham: выселения везде! Память оставалась в разумных пределах, основываясь на ограничении размера памяти, которое я установил. Вновь отменил его в режиме отладки, никаких выселений. CLRProfiler снова, выселения.
Наконец-то я заметил, что профилировщик заставил приложение работать без одновременная сборка мусора (также см. полезные Сопутствующий вопрос сбора мусора). Я отключил его в app.config и, конечно же, выселения!
Кажется, в лучшем случае возмутительная нехватка документации, чтобы не говорить: это работает только с неконкурентной сборкой мусора - , хотя я с момента ее портирования с ASP.NET им, возможно, не пришлось беспокоиться одновременная сборка мусора.
Так кто-нибудь еще видел это? Я хотел бы получить некоторые другие впечатления там и, возможно, более образованные идеи.
Обновление 1
Я воспроизвел проблему в рамках одного метода: кажется, что кэш должен быть написан параллельно для выключения выходов кэша (в режиме одновременного сбора мусора). Если есть какой-то интерес, я загружу тестовый код в публичное репо. Я определенно прохожу к глубокому концу пула CLR/GC/MemoryCache, и я думаю, что забыл о своих платах...
Обновление 2
Я опубликовал тестовый код на CodePlex, чтобы воспроизвести проблему. Также, возможно, интересный, исходный производственный код работает в Azure, как роль рабочего. Интересно, изменив настройку GC concurrency в роли app.config, не имеет никакого эффекта. Возможно, Azure переопределяет настройки GC так же, как ASP.NET? Кроме того, запуск тестового кода в WPF по сравнению с консольным приложением приведет к незначительным результатам выселения.
Ответы
Ответ 1
Вы можете "принудительно" собрать мусор сразу после проблемного метода и посмотреть, воспроизводится ли проблема:
System.Threading.Thread.Sleep(200);
GC.Collect();
GC.WaitForPendingFinalizers();
прямо в конце метода (убедитесь, что вы освобождаете любые дескрипторы для ссылки на объекты и обнуляете их). Если это предотвращает утечку памяти, а затем да, может быть ошибка времени выполнения.
Ответ 2
Коллекция мусора Stop-the-world основана на определении, существует ли сильная живая ссылка на объект в момент остановки мира. Параллельная сборка мусора обычно определяет, существует ли сильная живая ссылка на объект с определенного времени в прошлом. Моя гипотеза будет заключаться в том, что многие сильные ссылки на объекты, хранящиеся в WeakReferences, создаются индивидуально и отбрасываются. Если сборщик мусора "стоп-мир" срабатывает между временем создания определенного объекта и временем его отбрасывания, этот конкретный объект будет сохранен в живых, но ранее отброшенные объекты не будут. Напротив, одновременный сборщик мусора не может обнаружить, что все сильные ссылки на объект были отброшены до тех пор, пока не пройдет какое-то время без каких-либо сильных ссылок на создаваемый объект.
Я иногда хотел, чтобы .net предлагал что-то между сильной ссылкой и слабым, что предотвратило бы уничтожение объекта из памяти, но не защищало бы его от завершения или не имело бы слабых WeakReferences к нему недействительным. Такие ссылки немного усложняли бы процесс GC, требуя, чтобы у каждого объекта были отдельные флаги, указывающие на наличие сильных и квази-слабых ссылок на него, и было ли оно сканировано как для сильных, так и для квази-слабых ссылок, но такая функция может быть полезна во многих сценариях "слабых событий".
Ответ 3
Я нашел эту запись при поиске аналогичной темы, и я сосредоточен на исключении из памяти.
Если вы поместите объект в кеш, он все равно может ссылаться на другие объекты, и поэтому эти объекты не будут собираться в мусор, следовательно, исключение из памяти и, возможно, процессор будет привязан из-за сборки мусора Gen 2.
Вы помещаете "используемые" объекты в кэш или клоны "используемых" объектов в кеше? Если вы кладете клон в кеш, тогда "используемый" объект, который может ссылаться на другие объекты, может быть собран в мусор.
Если вы отключили механизм кэширования, ваша программа все еще не работает? Если у него не закончится память, это докажет, что объекты, которые вы в противном случае клали в кеш, все еще содержат ссылки на другие объекты, препятствующие сборке мусора.
Принудительная сборка мусора не является лучшей практикой и ее не нужно делать. В этом случае принудительная сборка мусора в любом случае не будет удалять ссылочные объекты.
Ответ 4
MemoryCache определенно имеет некоторые проблемы. Он съел 160 Мб памяти на моем сервере asp.net, просто изменился на простой список и добавил дополнительную логику для получения той же функциональности.