Подтвердить что ты не робот

В GC.Collect или нет?

Чтение старого, но классического документа Написание высокопроизводительных управляемых приложений - Primer, я наткнулся на следующий статус

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

Я работаю с приложениями, которые в данный момент времени потребляют много памяти. Когда я закончил с кодом, использующим эту память, я вызываю GC.Collect. Если я этого не сделаю, я получаю исключение из памяти. Такое поведение является непоследовательным, но грубо говоря, 30% времени, я получаю из памяти. После добавления GC.Collect я никогда не получал исключение из памяти. Является ли мое действие оправданным, даже несмотря на то, что этот документ с лучшей практикой не рекомендует ему?

4b9b3361

Ответ 1

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

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

Есть несколько случаев, когда сборщик мусора не всегда работает хорошо. Одна из них - куча большого объекта. Это специальное поколение для объектов размером более определенного размера (80 000 байт, IIRC, но это может быть старым сейчас). Это поколение почти никогда не собирается и почти никогда не уплотняется. Это означает, что со временем вы можете получить много значительных дыр в памяти, которые не будут выпущены. Физическая память фактически не используется и доступна для других процессов, но она по-прежнему потребляет адресное пространство внутри вашего процесса, из которого вы по умолчанию ограничены 2 ГБ.

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

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

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

Ответ 2

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

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

Но совет, который вы нашли, говорит о производительности. Вы заботитесь о производительности, если это заставляет ваше приложение работать в 30% случаев?

Ответ 3

Вообще говоря, GC.Collect не требуется. Если ваши изображения существуют в неуправляемой памяти, то обязательно используйте GC.AddMemoryPressure и GC.RemoveMemoryPressure соответственно.

Ответ 4

Из вашего описания это похоже на то, что объекты Dispose не располагаются, или вы не устанавливаете значения элементов, которые будут заменены на null до операции. В качестве примера последнего:

  • Получить таблицу, отобразить в сетке
  • (Обновляется загрузка пользователей)
  • Форма отключена во время обновления данных.
  • Запрос возвращается, новые данные заполняются в сетке

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