Понимание коллекции мусора в .NET.

Рассмотрим приведенный ниже код:

public class Class1 { public static int c; ~Class1() { c++; } } public class Class2 { public static void Main() { { var c1=new Class1(); //c1=null; // If this line is not commented out, at the Console.WriteLine call, it prints 1. } GC.Collect(); GC.WaitForPendingFinalizers(); Console.WriteLine(Class1.c); // prints 0 Console.Read(); } } 

Теперь, несмотря на то, что переменная c1 в основном методе выходит за пределы области действия и больше не ссылается на какой-либо другой объект, когда вызывается GC.Collect() , почему он не финализирован?

3 Solutions collect form web for “Понимание коллекции мусора в .NET.”

Вы тут спотыкались и делаете очень неправильные выводы, потому что используете отладчик. Вам нужно будет запустить свой код так, как он работает на компьютере пользователя. Перейдите в сборку Release сначала с помощью менеджера сборки + Configuration, измените комбинацию «Active solution configuration» в левом верхнем углу, чтобы «Отпустить». Затем перейдите в Инструменты + Параметры, Отладка, Общие и отключите опцию «Подавить оптимизацию JIT».

Теперь запустите свою программу снова и поработайте с исходным кодом. Обратите внимание, что дополнительные фигурные скобки не имеют никакого эффекта. И обратите внимание, что установка переменной в значение null не имеет никакого значения. Он всегда будет печатать «1». Теперь он работает так, как вы надеетесь, и ожидал, что это сработает.

Который уходит с задачей объяснить, почему он работает так по-разному, когда вы запускаете сборку Debug. Это требует объяснения того, как сборщик мусора обнаруживает локальные переменные и как это влияет на наличие отладчика.

Во-первых, джиттер выполняет две важные обязанности, когда компилирует IL для метода в машинный код. Первый из них очень заметен в отладчике, вы можете увидеть машинный код с окном Debug + Windows + Disassembly. Вторая обязанность, однако, полностью невидима. Он также генерирует таблицу, которая описывает, как используются локальные переменные внутри тела метода. В этой таблице есть запись для каждого аргумента метода и локальной переменной с двумя адресами. Адрес, в котором переменная сначала сохранит ссылку на объект. И адрес инструкции машинного кода, где эта переменная больше не используется. Также сохраняется ли эта переменная в кадре стека или в регистре процессора.

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

«Больше не использованный» адрес в таблице очень важен. Это делает сборщик мусора очень эффективным . Он может собирать ссылку на объект, даже если он используется внутри метода и этот метод еще не завершил выполнение. Что очень часто, ваш метод Main (), например, будет прекращать выполнение только до завершения вашей программы. Очевидно, что вам не нужны ссылки на объекты, используемые внутри этого метода Main (), чтобы жить в течение всего времени программы, что будет означать утечку. Джиттер может использовать таблицу, чтобы обнаружить, что такая локальная переменная больше не полезна, в зависимости от того, насколько далеко продвинулась программа внутри этого метода Main () до совершения вызова.

Почти магический метод, связанный с этой таблицей, – GC.KeepAlive (). Это очень специальный метод, он вообще не генерирует никакого кода. Его единственная обязанность – изменить эту таблицу. Он продлевает время жизни локальной переменной, предотвращая хранение ссылки, полученной от сбора мусора. Единственный раз, когда вам нужно использовать его, – это остановить GC от чрезмерного стремления к сбору ссылки, которая может произойти в сценариях взаимодействия, где ссылка передается на неуправляемый код. Сборщик мусора не может видеть, что такие ссылки используются таким кодом, поскольку он не был скомпилирован джиттером, поэтому нет таблицы, в которой говорится, где искать ссылку. Передача объекта делегата неуправляемой функции, такой как EnumWindows (), является примером шаблона, когда вам нужно использовать GC.KeepAlive ().

Итак, как вы можете сказать из вашего образца fragmentа после запуска его в сборке Release, локальные переменные могут быть собраны раньше, прежде чем закончить выполнение метода. Еще более мощно, объект может быть собран, пока один из его методов работает, если этот метод больше не ссылается на это . Существует проблема с этим, очень неудобно отлаживать такой метод. Так как вы можете поместить переменную в окно «Смотреть» или проверить ее. И он исчезнет, когда вы будете отлаживаться, если произойдет GC. Это было бы очень неприятно, поэтому дрожание знает, что есть отладчик. Затем он изменяет таблицу и изменяет «последний использованный» адрес. И изменяет его от нормального значения на адрес последней инструкции в методе. Который сохраняет переменную до тех пор, пока метод не вернулся. Это позволяет вам следить за ним до тех пор, пока метод не вернется.

Это теперь также объясняет, что вы видели раньше, и почему вы задали этот вопрос. Он печатает «0», потому что вызов GC.Collect не может собрать ссылку. В таблице указано, что переменная используется после вызова GC.Collect (), вплоть до конца метода. Вынужден сказать об этом, подключив отладчик и запустив сборку Debug.

Установка переменной в значение null имеет эффект теперь, потому что GC проверит переменную и больше не увидит ссылку. Но убедитесь, что вы не попадаете в ловушку, в которую попали многие программисты на C #, на самом деле написав этот код, было бессмысленно. Не имеет значения, присутствует ли этот оператор при запуске кода в сборке Release. Фактически, оптимизатор джиттера удалит это утверждение, поскольку он не имеет никакого эффекта. Поэтому не забудьте написать такой код, хотя это, казалось, имело эффект.


Одна заключительная заметка об этой теме – вот что заставляет программистов в беде писать небольшие программы, чтобы что-то делать с приложением Office. Отладчик обычно получает их на Неправильный путь, они хотят, чтобы программа Office выходила по требованию. Соответствующий способ сделать это – вызвать GC.Collect (). Но они обнаружат, что это не сработает, когда они отлаживают свое приложение, приводя их в никогда не землю, вызывая Marshal.ReleaseComObject (). Ручное управление памятью, оно редко работает должным образом, потому что они легко упускают из виду невидимую ссылку на интерфейс. GC.Collect () действительно работает, просто не при отладке приложения.

[Просто хотел добавить еще дальше процесс Internals of Finalization]

Таким образом, вы создаете объект, и когда объект собирается, должен быть вызван метод Finalize объекта. Но есть еще до финализации, чем это очень простое предположение.

КОРОТКИЕ КОНЦЕПЦИИ ::

  1. Объекты НЕ реализуют методы Finalize , там память немедленно восстанавливается, если, конечно, они не могут быть восстановлены
    код приложения больше

  2. Объекты, реализующие метод Finalize , концепцию / реализацию Application Roots , Finalization Queue Freacheable Queue прежде чем их можно будет вернуть.

  3. Любой объект считается мусором, если он не доступен для кода приложения

Предположим: classы / объекты A, B, D, G, H НЕ реализуют метод Finalize а C, E, F, I, J реализуют метод Finalize .

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

поэтому указатели на объекты C, E, F, I, J добавляются в очередь завершения.

Очередь финализации – это внутренняя структура данных, управляемая сборщиком мусора. Каждая запись в очереди указывает на объект, который должен иметь метод Finalize вызываемый до того, как память объекта может быть восстановлена. На рисунке ниже показана куча, содержащая несколько объектов. Некоторые из этих объектов доступны из корней приложения , а некоторые – нет. Когда были созданы объекты C, E, F, I и J, инфраструктура .Net обнаруживает, что эти объекты имеют методы Finalize и указатели на эти объекты, добавляются в очередь финализации .

введите описание изображения здесь

Когда происходит GC (первая коллекция), объекты B, E, G, H, I и J определяются как мусор. Поскольку A, C, D, F по-прежнему доступны в виде кода приложения, изображенного стрелками из желтой коробки выше.

Сборщик мусора просматривает очередь финализации, ища указатели на эти объекты. Когда указатель найден, указатель удаляется из очереди финализации и добавляется в свободную очередь («F-достижимая»).

Свободная очередь – это еще одна внутренняя структура данных, управляемая сборщиком мусора. Каждый указатель в freachable queue идентифицирует объект, который готов к Finalize метода Finalize .

После коллекции (1-я коллекция) управляемая куча выглядит примерно так, как показано на рисунке ниже. Объяснение, приведенное ниже:
1.) Память, занятая объектами B, G и H, была немедленно восстановлена, потому что у этих объектов не был метод finalize, который нужно было вызывать .

2.) Однако память, занятая объектами E, I и J, не может быть восстановлена, потому что их метод Finalize еще не был вызван. Вызов метода Finalize выполняется с помощью freacheable queue.

3.) A, C, D, F по-прежнему доступны в виде кода приложения, изображенного стрелками из желтой коробки выше, поэтому они НЕ будут собраны в любом случае

введите описание изображения здесь

Существует специальный stream времени выполнения, посвященный вызовам методов Finalize. Когда freachable queue пуст (как правило, это так), этот stream засыпает. Но когда появляются записи, этот stream просыпается, удаляет каждую запись из очереди и вызывает метод Finalize каждого объекта. Сборщик мусора сжимает возвращаемую память, и специальный stream времени запуска освобождает свободную очередь, выполняя метод Finalize каждого объекта. Итак, вот, наконец, когда ваш метод Finalize выполняется

В следующий раз, когда вызывается сборщик мусора (2-я assembly), он видит, что финализированные объекты действительно мусор, так как корни приложения не указывают на него, а лишняя очередь больше не указывает на него (это тоже ПУСТОЙ), поэтому память для объектов (E, I, J) просто восстанавливается из диаграммы Heap.See ниже и сравнивает ее с рисунком чуть выше

введите описание изображения здесь

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

ПРИМЕЧАНИЕ . Флашируемая очередь считается корнем, так же как глобальные и статические переменные являются корнями. Поэтому, если объект находится в freachable queue, тогда объект доступен и не является мусором.

В качестве последней заметки помните, что приложение для отладки – это одно, а Garbage Collection – это другое дело и работает по-другому. До сих пор вы не можете ЧУВСТВИТЬ сборку мусора, просто отлаживая приложения, а дальше, если вы хотите исследовать память, начните здесь.

Существует 3 способа управления памятью:

GC работает только для управляемых ресурсов, поэтому .NET предоставляет Dispose и Finalize для выпуска неуправляемых ресурсов, таких как stream, подключение к базе данных, объекты COM и т. Д.

1) Утилизировать

Dispose должен быть вызван явно для типов, которые реализуют IDisposable.

Программист должен вызвать это либо с помощью Dispose (), либо с помощью функции «Построение»

Используйте GC.SuppressFinalize (это), чтобы предотвратить вызов Finalizer, если вы уже использовали dispose ()

2) Завершить или отделить

Он называется неявно после того, как объект имеет право на очистку, финализатор для объектов вызывается последовательно по streamу финализатора.

Недостатком реализации финализатора является то, что его восстановление памяти задерживается, поскольку финализатор для такого classа / типов должен быть вызван предыдущей очисткой, поэтому дополнительный способ вернуть память.

3) GC.Collect ()

Использование GC.Collect () не обязательно устанавливает GC для сбора, GC все равно может переопределять и запускать, когда захочет.

также GC.Collect () будет запускать трассировочную часть сбора мусора и добавлять элементы в очередь финализатора, но не вызывать финализаторы для типов, которые обрабатываются другим streamом.

Используйте WaitForPendingFinalizers, если вы хотите убедиться, что все финализаторы были вызваны после вызова GC.Collect ()

Interesting Posts

Как отключить питание USB-портов, когда ноутбук находится в спящем режиме

Добавить драйверы сетевого принтера в Windows 7 / Server 2008 R2?

Как удалить приложения для Windows в Windows 10?

Настройка кнопок быстрого доступа в левой части File-> Open dialog в большинстве программ Windows

Есть ли ярлык для «Отметить все сообщения как прочитанные» в Outlook?

Получение имени предыдущего процесса

Шрифт Monaco не сглажен в некоторых приложениях GTK

Установка Windows на ноутбук с помощью внешнего монитора (основной дисплей сломан)

Помните расположение окон, когда они состыкованы и отстыкованы

Как заставить Mail.app завершить работу после того, как iCal отправит предупреждение?

Как я могу найти файл, который в настоящее время используется в качестве обоев в Windows 7?

192.168.1.1 случайным образом переключается между доступом к модему VDSL и доступом к беспроводному маршрутизатору.

Показать последние уведомления в Windows 8?

Windows XP / Vista / 7 Проверить заряд батареи от CMD?

Как вы компилируете проект из источника?

Давайте будем гением компьютера.