Вопрос

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

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

Этот вопрос:В этом случае, не было бы разумно вызвать GC .Собирать() перед переходом ко второму этапу?

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

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

Спасибо.

Это было полезно?

Решение

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

Правило №1

Не надо.

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

Правило №2

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

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

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

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

Измеряй, измеряй, измеряй.

Другие советы

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

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

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

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

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

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

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

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

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

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

Есть ситуации, когда это полезно, но в целом этого следует избегать.Вы могли бы сравнить это с ГОТО или ездой на мопеде:ты делаешь это, когда тебе нужно, но ты не рассказываешь об этом своим друзьям.

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

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

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

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

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

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

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

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

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

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

Раньше я часто получал исключение OutOfMemory, и я подумал, что было бы разумно периодически запускать GC.Сбор на основе переменной counter.Я увеличиваю счетчик, и когда достигается указанный уровень, вызывается GC для сбора любого мусора, который мог образоваться, и для восстановления любой памяти, потерянной из-за непредвиденных утечек памяти.

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

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

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

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

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

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

В нашей программе мы обрабатывали несколько электронных таблиц 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

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

Пол Смит

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top