Выигрывает ли сборщик мусора от нескольких вызовов функций Collect и WaitForPendingFinalizers()?

Я нашел этот код в Интернете, который добавляется после деинициализации объектов Excel Interop:

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();

Является ли это квази-нарушение DRY (вызов GC.Collect() и GC.WaitForPendingFinalizers() дважды подряд, заикаясь) каким-либо образом полезным или просто пустой тратой времени?


person B. Clay Shannon    schedule 23.06.2016    source источник
comment
См. комментарии к коду stackoverflow.com/a/8837636/1440057.   -  person Nikhil Vartak    schedule 23.06.2016
comment
Если у объекта есть финализатор, то должно произойти как минимум две коллекции, прежде чем память будет полностью освобождена. Первая коллекция просто помещает ее в очередь на финализацию. Второй увидит, что в очереди финализации больше нет ссылки, а финализатор уже запущен, поэтому его можно восстановить.   -  person Mike Zboray    schedule 23.06.2016
comment
@mikez: Итак, поскольку вы написали ... как минимум два ... есть ли польза от третьей итерации? Каков верхний предел, когда процессор просто гоняется за своим хвостом?   -  person B. Clay Shannon    schedule 23.06.2016
comment
Будьте очень критичны при использовании такого кода. См. обсуждение в этом и этот ТАК вопрос.   -  person Jeroen Heier    schedule 23.06.2016
comment
Нет, третий здесь не нужен, так как GC.Collect() собирает все поколения. Я имел в виду сборку мусора в более широком смысле, которая может быть ограничена определенными поколениями. Технически объект пережил сборку, будучи помещенным в очередь на завершение. Теперь это поколение 1 или поколение 2. Коллекция поколения 0 не увидит, что она восстановлена, даже если был запущен финализатор. Таким образом, 2 коллекции любого поколения — это минимум.   -  person Mike Zboray    schedule 23.06.2016


Ответы (3)


Это из примера программы Microsoft документация сайта.

   //Force garbage collection.
   GC.Collect();

   // Wait for all finalizers to complete before continuing.
   // Without this call to GC.WaitForPendingFinalizers, 
   // the worker loop below might execute at the same time 
   // as the finalizers.
   // With this call, the worker loop executes only after
   // all finalizers have been called.
   GC.WaitForPendingFinalizers();

When the garbage collector finds objects that can be reclaimed, it checks each object to determine the object's finalization requirements. If an object implements a finalizer and has not disabled finalization by calling SuppressFinalize, the object is placed in a list of objects that are marked as ready for finalization. The garbage collector calls the Finalize methods for the objects in this list and removes the entries from the list. This method blocks until all finalizers have run to completion.

The thread on which finalizers are run is unspecified, so there is no guarantee that this method will terminate. However, this thread can be interrupted by another thread while the WaitForPendingFinalizers method is in progress. For example, you can start another thread that waits for a period of time and then interrupts this thread if this thread is still suspended.

Из того, что я могу сказать, нет явных указаний на преимущество двойного вызова.

person Matt LaCrosse    schedule 19.09.2017

Выигрывает ли сборщик мусора от нескольких вызовов...

Вы делаете это не для того, чтобы принести пользу сборщику мусора, а вовсе не для того, чтобы заставить Excel.exe завершить работу. Он не может остановиться, пока все оболочки взаимодействия (RCW) не будут завершены. Одного вызова GC.Collect() достаточно, чтобы поезд тронулся.

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

Вторые вызовы Collect+Wait бесполезны. RCW очень малы, поэтому вызов Collect не освобождает полезный объем памяти. Ждать нечего ждать.

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

person Community    schedule 01.03.2018
comment
Я уже сказал, что назначу награду Мэтту, что я и сделал, но я посмотрю, смогу ли я назначить еще одну награду здесь. Если я могу, я буду. - person B. Clay Shannon; 02.03.2018
comment
@B.ClayShannon, возможно ли присудить вознаграждение за ответ вики сообщества? - person Evk; 02.03.2018
comment
@Evk Не уверен, о чем ты спрашиваешь. Можно ли присуждать награды? Да, я делал это миллионы раз. Однажды у меня было 13 000 баллов. - person B. Clay Shannon; 03.03.2018
comment
@B.ClayShannon нет, я имел в виду именно ответы сообщества вики, подобные этому. Но я уже нашел, что это возможно. - person Evk; 03.03.2018
comment
@Evk Я вижу; Я не знал, что человек может быть вики-сообществом; звучит как гавайская фиеста или около того. - person B. Clay Shannon; 04.03.2018

Короткий ответ: нет, вам не нужно вызывать его дважды.

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

Когда GC запускается, он восстанавливает все объекты из текущего поколения, на которые нет сильных* ссылок (* - можно использовать тип System.WeakReference, чтобы иметь ссылку на объект, позволяя GC восстанавливать его). Те объекты, которые пережили сборку мусора, переходят в следующее поколение. Если следующее поколение будет достаточно заполнено, сборщик мусора запустится и на нем, перемещая уцелевшие объекты дальше. Сказав это, вы можете видеть, что запуск GC во второй раз, скорее всего, не будет иметь никакого значения.

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

person Artak    schedule 02.03.2018