Сборка мусора не уменьшает текущее использование памяти - в режиме выпуска. Почему?

Я создал быструю программу, которой нужно было перебрать огромный файл журнала (пара миллионов записей) и найти различные фрагменты изнутри. Поскольку объем данных был настолько огромным, мне было любопытно посмотреть на вкладку производительности диспетчера задач Windows и посмотреть, сколько процессора и памяти используются.

Очевидно, что после того, как программа успешно получит мои результаты, загрузка ЦП резко снизится. Но после того, как я посмотрел, как мое использование памяти медленно увеличивается до нескольких гигабайт во время выполнения, оно остается прежним.

Я попытался вызвать GC.Collect () в конце моей функции, попытался установить значение null, попытался запустить программу в режиме Release (я слышал, что GC.Collect () может работать не так, как я хочу, при отладке Режим).

Использование памяти сокращается, если я закрою программу, но я не могу понять, почему я не могу очистить свое приложение в течение всего срока службы приложения. В конце концов, это одноразовое приложение, но мне просто любопытно узнать, что мне не хватает, то есть что удерживает всю эту память.

Мысли?


person Alex York    schedule 20.05.2009    source источник


Ответы (10)


Сборщик мусора не гарантированно запускается при вызове Collect (). Он просто помечает объект для сбора. При следующем запуске GC он «соберет» помеченный объект.

В .NET нет способа заставить сборщик мусора собирать данные в определенный момент времени - если вам нужна эта функция, вам нужно будет перейти к собственному коду.

person rein    schedule 20.05.2009
comment
Я использовал этот код для принудительной сборки мусора. Особенно при работе с COM-объектами: GC.Collect () GC.WaitForPendingFinalizers () - person amrtn; 20.05.2009
comment
Спасибо. Под собственным кодом вы имеете в виду неуправляемый C # или использование чего-то вне управляемой среды .NET? - person Alex York; 20.05.2009
comment
Что-то вне .NET. C ++ или аналогичный. - person rein; 20.05.2009

Вызов GC.Collect () - это всего лишь запрос на сбор мусора, а не заказ, поэтому среда CLR может проигнорировать запрос, если сочтет нужным.

Например, если существует много мусора, который может вызвать заметную задержку во время сбора, но все еще есть свободная память для обслуживания последующих выделений, то сборщик мусора может проигнорировать ваш запрос.

person Sean    schedule 20.05.2009
comment
На самом деле GC.Collect () по умолчанию является принудительным сборщиком мусора, который будет прямо в этот момент запустить поток сборки сборщика мусора. Вы можете приостановить собственный поток с помощью GC.WaitForPendingFinalizers. - person Leonidas; 20.05.2009
comment
Спасибо, Шон, я подозревал, что это так, но ты это подтвердил. Я даю другому парню правильный ответ, объясняя, как это исправить, если я захочу. - person Alex York; 20.05.2009
comment
Действительно? Насколько я понимаю, документация вызывает вызов GC для всех поколений, но это не означает, что он будет собирать мусор. Все, если говорит, - это смотреть на все поколения, независимо от того, как долго они были в памяти. Это не то же самое, что убирать мусор. - person Sean; 20.05.2009

Для многих распределителей памяти (со сборкой мусора или без нее) нормально "накапливать" освобожденную память, а не возвращать ее ОС, потому что во многих ситуациях только что освобожденная память будет запрашиваться снова, и это быстрее. чтобы сохранить его в процессе, а не возвращать его и снова и снова просить ОС (возвращение и т. д. также требует дополнительных усилий из-за проблем с выравниванием страниц и т.п.).

Как вы упомянули, в одноразовом приложении это не проблема. Когда у меня есть долго работающее приложение, которое, как я знаю, будет временным требованием для большого количества памяти, один из подходов, которые я использую, - это создать подпроцесс для выполнения требующих памяти вещей, поскольку, когда этот процесс завершает работу ОС вернет память. Это проще в системах Unix (просто форк), но не так уж плохо и в Windows, если это необходимо.

person Alex Martelli    schedule 20.05.2009

Есть выход. Я нашел эту статью, она на испанском языке, уверен, что вы понимаете код, она работает, я протестировал [http://www.nerdcoder.com/c-net-forzar-liberacion-de-memoria-de-nuestras-aplicaciones/%5d%5b1%5d

[DllImport("kernel32.dll", EntryPoint = "SetProcessWorkingSetSize", ExactSpelling = true, CharSet = CharSet.Ansi, SetLastError = true)]
private static extern int SetProcessWorkingSetSize(IntPtr process, int minimumWorkingSetSize, int maximumWorkingSetSize);
public static void alzheimer()
{
    GC.Collect();
    GC.WaitForPendingFinalizers();
    SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1);
}
person Gilbert Figueroa    schedule 13.05.2013
comment
При правильном ответе соответствующая информация копируется сюда, в сам вопрос, а затем размещается ссылка на справочную статью, особенно если учесть, что она не на английском языке. - person JoshDM; 13.05.2013
comment
Проголосовали за этот ответ, хотя и отсутствуют подробности, но на самом деле работают - person ChaseMedallion; 12.05.2014
comment
Что ж, ссылка теперь мертва, и это мне ни к чему. - person ygoe; 15.08.2016
comment
Если я попытаюсь выделить почти всю свободную оперативную память, а затем избавиться от буферов, выйдя из using() и применив этот метод, он освободит оперативную память, но если я попытаюсь найти свободную оперативную память с помощью GlobalMemoryStatusEx(), он вернется намного ниже того, что показывает TaskMan, и попытаюсь выделить близко даже к тому, что приложение вылетает из строя. Значит, что-то все еще не так. - person ajeh; 15.01.2018

Возможно, ваши объекты все еще как-то укоренились. Вы можете попробовать использовать профилировщик памяти, чтобы узнать, так ли это. Обычно для этого я рекомендую .NET Memory Profiler от SciTech, но Red-Gate также имеет достойный профилировщик памяти. У SciTech и Red-Gate есть пробные версии. Также возможно использование WinDBG с SOS.

здесь.

person Jack Bolding    schedule 20.05.2009

Не забывайте, что CLR в конечном итоге использует базовый диспетчер памяти в ядре Windows, у которого могут быть свои собственные политики относительно того, когда освобождать память. Например, если используемая вами память поступает из пула, она может быть просто возвращена в пул, а не освобождена, когда вы освобождаете ее на уровне пользователя. Поэтому вполне возможно, что может быть (и, вероятно, будет) несоответствие между объемом памяти, который хранится в вашем приложении, и объемом памяти, выделенной процессу.

person MattK    schedule 20.05.2009

Метод Collect не гарантирует освобождение всей недоступной памяти.

person Gordian Yuan    schedule 20.05.2009

чтобы заставить GC.Collect выполнять сбор, назовите его так: GC.Collect (2); это означает, что вы просите GC выполнить полную блокировку GC для всей кучи, 2 означает поколение 2 вашей кучи.

person mfawzymkh    schedule 04.06.2009
comment
Согласно документации, сборка без параметров точно такая же, как принудительная сборка мусора для всех поколений. docs.microsoft.com/en- us / dotnet / api / - person Homulvas; 04.11.2019

Вы должны смотреть на счетчики производительности для размеров кучи CLR, а не на общую память, выделенную для процесса, показанного в диспетчере задач. Windows не будет восстанавливать память, выделенную для процесса, если системе в целом не хватает памяти, даже если процесс ни для чего не использует память.

person Jonas Høgh    schedule 20.05.2009

Реализует ли какой-либо из объектов, используемых в вашем цикле, IDisposable?

person Marc Charbonneau    schedule 20.05.2009