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

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

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

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

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

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

Спасибо.

Ответы [ 20 ]

6 голосов
/ 23 октября 2012

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

Есть абсолютно необходимое время для вызова GC.Collect.

5 голосов
/ 13 мая 2012

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

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

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

4 голосов
/ 05 февраля 2014

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

В нашей программе мы обрабатывали таблицы 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().

Пол Смит

4 голосов
/ 04 апреля 2017

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

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

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

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

2 голосов
/ 19 октября 2008

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

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

2 голосов
/ 25 сентября 2008

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

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

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

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

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

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

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

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

1 голос
/ 17 августа 2012

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

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

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

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

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

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

...