Поведение сборки мусора в .NET (с объектом DataTable) - PullRequest
3 голосов
/ 12 января 2011

Мне интересно, почему после создания очень простой DataTable, а затем установки его в null, сборщик мусора не очищает всю память, используемую этой DataTable.Вот пример.Переменная Before должна быть равна Removed, но это не так.

{
 long Before = 0, After = 0, Removed = 0, Collected = 0;

 Before = GC.GetTotalMemory(true);
 DataTable dt = GetSomeDataTableFromSql();
 After = GC.GetTotalMemory(true);
 dt = null;
 Removed = GC.GetTotalMemory(true);
 GC.Collect();
 Collected = GC.GetTotalMemory(true);
}

Дает следующие результаты.

Before = 388116
After = 731248
Removed = 530176
Collected = 530176

Ответы [ 6 ]

5 голосов
/ 12 января 2011

Несколько причин:

GC работает в свое приятное время; обычно, когда время выполнения не хватает памяти. Вот почему удаление объектов, таких как соединения с БД, важно; да, они будут выпущены в конце концов, но не раньше, чем GC соизволит бежать.

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

GC работает в своем собственном потоке. Поэтому информация, предоставляемая статическими методами GC, основана на том, что доступно вызывающей стороне во время ее вызова. Вы вызываете GetTotalMemory в последний раз, когда GC все еще работает, или, может быть, даже до того, как он даже запустился, и поэтому показатели памяти не были обновлены с учетом того, что GC завершает.

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

4 голосов
/ 12 января 2011

GC.Collect(); - это всего лишь предложение сборщику мусора, что могут быть объекты, которые необходимо очистить.GC работает по собственному расписанию, и очень редко ему требуется приглашение GC.Collect();.

Шансы увидеть воздействие на память при немедленном вызове GC.Collect(); (микросекунд) после освобождения ресурса невелики.

Также: объект DataTable не является особенным вглаза GC.Любой ссылочный тип в .NET будет обрабатываться GC таким же образом.

2 голосов
/ 13 января 2011

Документация для GC.GetTotalMemory гласит:

Сборщик мусора не гарантирует, что будет собрана вся недоступная память.

Предлагаетчто он будет блокироваться только на короткий промежуток времени, чтобы дождаться завершения сборки мусора и финализаторов. этот ответ SO объясняет, что DataTables не содержат никаких управляемых ресурсов и подавляют завершение, поэтому вам не нужно вызывать GC.WaitForPendingFinalizers для восстановления памяти.

Другая возможность состоит в том, что dt не имеет права на сбор при вызове GC.Collect - если есть член класса или родитель DataSet, содержащий ссылку на него, то он не может быть собран.

Кроме того, в отличие от некоторых издругие ответы GC.Collect вызывают немедленный сбор (а не просто «подсказку») - документация гласит:

Запускает немедленный сбор мусора для всех поколений.

В этой статье также говорится, что дело обстоит именно так - в разделе «Условия для сбора мусора» одна из трех возможностей:

Метод GC.Collect вызывается.Почти во всех случаях вам не нужно вызывать этот метод, потому что сборщик мусора работает непрерывно.Этот метод в основном используется для уникальных ситуаций и тестирования.

1 голос
/ 12 января 2011

Согласно http://msdn.microsoft.com/en-us/library/xe0c2357.aspx:

Используйте этот метод, чтобы попытаться восстановить всю недоступную память.

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

Таким образом, возможно, что вызов Collect () не обязательно приведет к тому, что вы ожидаете, что он немедленно произведет,

1 голос
/ 12 января 2011

В документации по сборке мусора в .NET ВСЕГДА указывалось, что она не дает никаких гарантий относительно того, когда происходит сбор.

http://msdn.microsoft.com/en-us/magazine/bb985010.aspx

http://msdn.microsoft.com/en-us/library/ee787088.aspx

http://www.simple -talk.com / dotnet / .net-framework / понимание-garbage-collection-in-.net / - - Это хорошая статья для объяснения сборки мусора с хорошими диаграммами для облегчить понимание.

выдержка из этой последней статьи, относящейся к вашему вопросу:

Если объект имеет финализатор, он не сразу удаляется, когда сборщик мусора решает, что это не дольше "жить". Вместо этого он становится особый вид рута, пока называется методом финализатора. это означает, что эти объекты обычно требуется более одного мусора коллекция должна быть удалена из памяти, как они выживут в первый раз они признаны неиспользованными.

0 голосов
/ 12 января 2011

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

Поскольку вы обращаетесь к SQL, в соответствии с вашим методом GetSomeDataTableFromSql () у вас могут быть некоторые кэшированные экземпляры соединения SQL и другие объекты, о которых вы не знаете напрямую.

Таким образом, выделение экземпляра DataTable и последующее избавление от него не вернут вас к тому же уровню выделения памяти, который у вас был.

...