Что такого плохого в использовании GC.Collect ()? - PullRequest
100 голосов
/ 23 сентября 2008

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

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

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

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

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

Спасибо.

Ответы [ 20 ]

85 голосов
/ 23 сентября 2008

Из блога Рико ...

Правило № 1

Не.

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

Правило № 2

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

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

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

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

Мера, мера, мера.

56 голосов
/ 23 сентября 2008

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

23 голосов
/ 12 января 2012

Так как насчет того, когда вы используете 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?

15 голосов
/ 23 сентября 2008

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

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

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

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

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

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

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

12 голосов
/ 23 сентября 2008

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

12 голосов
/ 23 сентября 2008

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

10 голосов
/ 23 сентября 2008

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

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

9 голосов
/ 23 сентября 2008

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

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

7 голосов
/ 23 сентября 2008

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

6 голосов
/ 20 марта 2013

Фактически, я не думаю, что называть 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).
...