Что плохого в использовании GC.Collect()?

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

Скажем, я разрабатываю приложение, в котором использование памяти сильно зависит от того, что делает пользователь. Жизненный цикл приложения можно разделить на два основных этапа: редактирование и обработка в реальном времени. На этапе редактирования предположим, что созданы миллиарды или даже триллионы объектов; некоторые из них небольшие, а некоторые из них нет, у некоторых могут быть финализаторы, а некоторые - нет, и предполагается, что их продолжительность жизни варьируется от нескольких миллисекунд до долгих часов. Затем пользователь решает переключиться на этап реального времени. На этом этапе предположим, что производительность играет фундаментальную роль, и малейшее изменение в потоке программы может привести к катастрофическим последствиям. Затем создание объекта сводится к минимуму, используя пулы объектов и такие, но затем, GC совершает неожиданные перехваты и бросает все это, а кто-то умирает.

Вопрос: В этом случае было бы разумно вызвать GC.Collect() перед входом на второй этап?

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

Примечание. Как некоторые из вас указали,.NET не может быть лучшей платформой для такого приложения, но это выходит за рамки этого вопроса. Цель состоит в том, чтобы выяснить, может ли вызов GC.Collect() улучшить общее поведение/производительность приложения или нет. Мы все согласны с тем, что обстоятельства, при которых вы бы делали такие вещи, были крайне редки, но, опять же, GC пытается угадать и делает это отлично, большую часть времени, но это все еще касается угадывания.

Спасибо.

Ответ 1

Из Rico Blog...

Правило № 1

Не.

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

Правило № 2

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

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

Таким образом, похоже, что эта ситуация может подпадать под правило №2, вы знаете, что есть момент времени, когда много старых объектов умерло, и это не повторяется. Однако не забывайте про слова Rico.

Правило № 1 должно превзойти Правило № 2 без убедительные доказательства.

Измерение, измерение, измерение.

Ответ 2

Если вы вызываете GC.Collect() в производственном коде, вы, по сути, заявляете, что знаете больше, чем авторы GC. Это может быть так. Однако обычно это не так, и поэтому сильно не рекомендуется.

Ответ 3

Так как насчет того, когда вы используете COM-объекты, такие как MS Word или MS Excel из .NET? Не вызывая GC.Collect после освобождения COM-объектов, мы обнаружили, что экземпляры приложений Word или Excel все еще существуют.

На самом деле код, который мы используем:

Utils.ReleaseCOMObject(objExcel)

' Call the Garbage Collector twice. The GC needs to be called twice in order to get the
' Finalizers called - the first time in, it simply makes a list of what is to be finalized,
' the second time in, it actually does the finalizing. Only then will the object do its 
' automatic ReleaseComObject. Note: Calling the GC is a time-consuming process, 
' but one that may be necessary when automating Excel because it is the only way to 
' release all the Excel COM objects referenced indirectly.
' Ref: http://www.informit.com/articles/article.aspx?p=1346865&seqNum=5
' Ref: http://support.microsoft.com/default.aspx?scid=KB;EN-US;q317109
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
GC.WaitForPendingFinalizers()

Так что это будет неправильное использование сборщика мусора? Если так, как мы можем заставить объекты Interop умереть? Кроме того, если он не предназначен для такого использования, почему метод GC Collect даже Public?

Ответ 4

Ну, GC - одна из тех вещей, с которыми у меня связаны отношения любви/ненависти. У нас сломал его в прошлом через VistaDB и написал об этом в блоге. Они исправили это, но для получения исправлений от таких вещей требуется много времени.

GC сложный, и один подход подходит ко всему, очень и очень сложно сделать что-то такое большое. MS неплохо справилась с этим, но иногда можно обманывать GC.

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

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

Следите за тем, сколько времени, проведенного в GC, через счетчики производительности системы. Если вы видите, что ваше приложение использует 20% или более своего времени в GC, у вас серьезные проблемы с управлением объектами (или ненормальная схема использования). Вы хотите всегда минимизировать время, затрачиваемое GC, потому что оно ускорит ваше приложение.

Также важно отметить, что GC на серверах отличается от рабочих станций. Я видел несколько небольших проблем для отслеживания проблем с людьми, которые не тестировали их обоих (или даже не знают, что их двое из них).

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

Ответ 5

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

Ответ 6

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

Ответ 7

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

Конечно, вы должны прокомментировать это и убедиться сами.

Ответ 8

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

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

Ответ 9

Вызов GC.Collect() заставляет CLR выполнять стек, чтобы увидеть, может ли каждый объект быть действительно освобожден, проверяя ссылки. Это повлияет на масштабируемость, если число объектов велико, а также, как известно, слишком часто запускает сборку мусора. Доверяйте CLR и пусть сборщик мусора запускается сам по себе.

Ответ 10

Создание изображений в цикле - даже если вы вызываете dispose, память не восстанавливается. Мусор собирают каждый раз. Я перешел с 1,7 ГБ памяти на мое приложение для обработки фотографий до 24 МБ, и производительность отличная.

Абсолютное время, которое вам нужно вызвать GC.Collect.

Ответ 11

В .net время, необходимое для выполнения сборки мусора, гораздо сильнее связано с количеством вещей, которые не являются мусором, чем количеством материала, который есть. В самом деле, если объект не переопределяет Finalize (либо явно, либо через С# destructor), является объектом WeakReference, находится на большой куче объекта или является специальным в каком-либо другом gc-связанном способе, единственное, что идентифицирует память, в которой он сидит как объект, - это наличие укорененных ссылок на нее. В противном случае операция GC аналогична тому, чтобы вывести из здания все значение и динамизировать здание, построить новый на месте старого и поместить в него все ценные предметы. Усилия, необходимые для динамизации здания, полностью независимы от количества мусора внутри него.

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

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

Ответ 12

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

Вся операция занимает довольно много памяти, и она не уверена относительно количества строк и размера содержимого файла в таблицах.

Я часто получал OutofMemory Exception и думал, что будет разумно периодически запускать GC.Collect на основе переменной счетчика. Я увеличиваю счетчик, и когда достигается указанный уровень, вызывается GC для сбора любого мусора, который мог образоваться, и для восстановления любой памяти, потерянной из-за непредвиденных утечек памяти.

После этого, я думаю, что это работает хорошо, по крайней мере, не исключение !!!
Я звоню следующим образом:

var obj = /* object utilizing the memory, in my case Form itself */
GC.Collect(GC.GetGeneration(obj ,GCCollectionMode.Optimized).

Ответ 13

У нас была аналогичная проблема с сборщиком мусора, который не собирал мусор и не освобождал память.

В нашей программе мы обрабатывали таблицы Excel с умеренным размером с OpenXML. Таблицы содержали от 5 до 10 "листов" с примерно 1000 строк из 14 столбцов.

Программа в 32-разрядной среде (x86) выйдет из строя с ошибкой "вне памяти". Мы получили его для работы в среде x64, но нам нужно лучшее решение.

Мы нашли один.

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

Вызов GC изнутри подпрограммы не работал. Память никогда не возвращалась...

 
For Each Sheet in Spreadsheets
    ProcessSheet(FileName,sheet)
Next

Private Sub ProcessSheet(ByVal Filename as string, ByVal Sheet as string)
    ' open the spreadsheet 
    Using SLDoc as SLDocument = New SLDocument(Filename, Sheet)
        ' do some work....
        SLDoc.Save
    End Using
    GC.Collect()
    GC.WaitForPendingFinalizers()
    GC.Collect()
    GC.WaitForPendingFinalizers()
End Sub

Перемещая вызов GC за пределами области действия подпрограммы, мусор был собран и память была освобождена.

 
For Each Sheet in Spreadsheets
    ProcessSheet(FileName,sheet)
    GC.Collect()
    GC.WaitForPendingFinalizers()
    GC.Collect()
    GC.WaitForPendingFinalizers()
Next

Private Sub ProcessSheet(ByVal Filename as string, ByVal Sheet as string)
    ' open the spreadsheet 
    Using SLDoc as SLDocument = New SLDocument(Filename, Sheet)
        ' do some work....
        SLDoc.Save
    End Using
End Sub

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

Пол Смит

Ответ 14

Нет ничего плохого в явном вызове коллекции. Некоторые люди просто хотят верить, что если это услуга, предоставляемая поставщиком, не ставьте под сомнение. О, и все эти случайные зависания в неправильные моменты вашего интерактивного приложения? Следующая версия сделает это лучше!

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

Вы когда-нибудь отвечали на открытый вопрос, такой как "который является лучшим алгоритмом сортировки" с окончательным ответом? Если это так, не прикасайтесь к GC. Для тех из вас, кто попросил условия или дал ответы типа "в этом случае", вы можете продолжить изучение GC и когда его активировать.

Должен сказать, что у меня было замораживание приложений в Chrome и Firefox, которые отвлекают меня от меня, и даже тогда в некоторых случаях память растет беспрепятственно - если бы они научились вызывать сборщик мусора - или дал мне кнопку, так что, когда я начинаю читать текст страницы, я могу ударить ее и, таким образом, не зависнуть в течение следующих 20 минут.

Ответ 15

Я думаю, что вы правы в отношении сценария, но я не уверен в API.

Microsoft заявляет, что в таких случаях вы должны добавить давление памяти в качестве подсказки для GC, чтобы она вскоре выполнила сбор.

Ответ 16

Что с ним? Тот факт, что вы во второй раз угадываете сборщик мусора и распределитель памяти, который между ними имеет гораздо большее представление о фактическом использовании памяти вашего приложения во время выполнения, чем вы.

Ответ 17

Желание вызвать GC.Collect() обычно пытается скрыть ошибки, которые вы сделали где-то еще!

Было бы лучше, если бы вы обнаружили, где вы забыли уничтожить вещи, которые вам больше не нужны.

Ответ 18

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

Ответ 19

Сама .NET Framework никогда не была разработана для работы в среде реального времени. Если вам действительно нужна обработка в реальном времени, вы либо используете встроенный язык реального времени, который не основан на .NET, либо использует .NET Compact Framework, работающую на устройстве Windows CE.

Ответ 20

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

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

В обоих случаях результат был простым: Нет GC.Collect, вне памяти, последовательно; GC.Collect, безупречная производительность.

Я попытался решить проблемы с памятью несколько раз, но безрезультатно. Я взял его.

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

Единственное, что может случиться с этим, - это когда вы моралистизируете его. Это не проблема ценностей; многие программисты умерли и отправились прямо на небеса со многими ненужными GC.Collects в своем коде, который их переживает.