Наилучшая практика принудительного сбора мусора в C#

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

15 ответов


лучшая практика-не заставлять сборку мусора.

согласно MSDN:

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

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

просто попробуйте убедиться, что объекты убрали, когда они тебе больше не нужны. Если у вас есть настраиваемые объекты, посмотрите на использование "оператора using" и интерфейса IDisposable.

эта ссылка имеет некоторые хорошие практические советы в отношении освобождения памяти / сбора мусора и т. д.:

http://msdn.microsoft.com/en-us/library/66x5fx1b.aspx


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

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

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

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

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

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

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

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

Смотрите также "выступление Рико Мариани лакомые кусочки"


посмотрите на это так - эффективнее ли выбрасывать кухонный мусор, когда мусорный бак находится на 10% или пусть он заполняется, прежде чем вынимать его?

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

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

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


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


есть несколько общих принципов в программировании, которые являются абсолютными. В половине случаев, когда кто-то говорит: "Вы делаете это неправильно", он просто извергает определенное количество догм. В C это был страх перед такими вещами, как самомодифицирующийся код или потоки, в языках GC он заставляет GC или, альтернативно, предотвращает запуск GC.

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

проблемы программирования широко варьируются и требуют гибкого подхода. Я видел случаи, когда имеет смысл блокировать GC на языках сбора мусора и местах, где имеет смысл запускать его, а не ждать его происходить естественным образом. В 95% случаев любой из них был бы знаком того, что он не подошел к проблеме правильно. Но 1 раз из 20, вероятно, есть действительный случай, чтобы сделать для него.


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


один случай, с которым я недавно столкнулся, требовал ручных вызовов GC.Collect() был при работе с большими объектами C++, которые были обернуты в крошечные управляемые объекты C++, которые, в свою очередь, были доступны с C#.

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


Не уверен, что это лучшая практика, но при работе с большим количеством изображений в цикле (т. е. создание и удаление большого количества объектов Graphics/Image/Bitmap) я регулярно позволяю GC.Собирать.

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


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

  1. У вас есть что-то в вашем коде, которое объявляет элементы в большем объеме, чем необходимо
  2. - это использование памяти слишком высокие
  3. сравните производительность до и после использования GC.Collect (), чтобы увидеть, если это действительно помогает.

предположим, что ваша программа не имеет утечки памяти, объекты накапливаются и не могут быть GC-ed в Gen 0, потому что: 1) они ссылаются на долгое время, поэтому попадают в Gen1 & Gen2; 2) они являются большими объектами (>80K), поэтому попадают в LOH (большая куча объектов). И LOH не делает компактировать как в Gen0, Gen1 & Gen2.

Проверьте счетчик производительности ".NET Memory" вы можете видеть, что 1) проблема действительно не проблема. Как правило, каждые 10 Gen0 GC сработает 1 Gen1 и ГХ, и каждые 10 Gen1 GC вызовет 1 Gen2 GC. Теоретически, GC1 и GC2 никогда не могут быть GC-ed, если нет давления на GC0 (если использование памяти программы действительно подключено). Со мной такого никогда не случалось.

для Проблемы 2), Вы можете проверить счетчик производительности ".NET Memory", чтобы проверить, не раздувается ли LOH. Если это действительно проблема для вашей проблемы, возможно, вы можете создать пул больших объектов, как предлагает этот блог http://blogs.msdn.com/yunjin/archive/2004/01/27/63642.aspx.


большие объекты выделяются в LOH (куча больших объектов), а не в gen 0. Если вы говорите, что они не собирают мусор с gen 0, вы правы. Я считаю, что они собираются только тогда, когда происходит полный цикл GC (поколения 0, 1 и 2).

Это, как говорится, Я считаю, что с другой стороны GC будет регулировать и собирать память более агрессивно, когда вы работаете с большими объектами, и давление памяти растет.

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


Я хотел бы добавить, что: Вызываю ГК.Collect () (+WaitForPendingFinalizers ()) является одной из частей истории. Как справедливо отметили другие, GC.COllect () является недетерминированной коллекцией и оставляется на усмотрение самого GC (CLR). Даже если вы добавляете вызов WaitForPendingFinalizers, он может не быть детерминированным. Возьмите код из этого msdn ссылке и запустите код с итерацией цикла объекта как 1 или 2. Вы найдете, что означает недетерминированный (установите точку останова в деструкторе объекта). Точно, деструктор не вызывается, когда было только 1 (или 2) задерживающихся объекта по ожиданию..().[Цитата reqd.]

Если ваш код имеет дело с неуправляемыми ресурсами (например, внешними дескрипторами файлов), необходимо реализовать деструкторы (или финализаторы).

вот интересный пример:

Примечание: если вы уже пробовали приведенный выше пример из MSDN, следующий код очистит воздух.

class Program
{    
    static void Main(string[] args)
        {
            SomePublisher publisher = new SomePublisher();

            for (int i = 0; i < 10; i++)
            {
                SomeSubscriber subscriber = new SomeSubscriber(publisher);
                subscriber = null;
            }

            GC.Collect();
            GC.WaitForPendingFinalizers();

            Console.WriteLine(SomeSubscriber.Count.ToString());


            Console.ReadLine();
        }
    }

    public class SomePublisher
    {
        public event EventHandler SomeEvent;
    }

    public class SomeSubscriber
    {
        public static int Count;

        public SomeSubscriber(SomePublisher publisher)
        {
            publisher.SomeEvent += new EventHandler(publisher_SomeEvent);
        }

        ~SomeSubscriber()
        {
            SomeSubscriber.Count++;
        }

        private void publisher_SomeEvent(object sender, EventArgs e)
        {
            // TODO: something
            string stub = "";
        }
    }

Я предлагаю сначала проанализировать, каким может быть выход, а затем запустить, а затем прочитать причину ниже:

{деструктор вызывается неявно только после завершения программы. } Для детерминированной очистки объекта необходимо реализовать IDisposable и сделать явный вызов Dispose(). Вот в чем суть! :)


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

.NET GC хорошо разработан и настроен для адаптации, что означает, что он может настроить порог GC0/1/2 в соответствии с "привычкой" использования памяти программы. Таким образом, он будет адаптирован к вашей программе через некоторое время. После вызова GC.Соберите явно, пороги будут сброшены! И .NET должен потратить время, чтобы адаптироваться к вашему опять "привычка" программы.

мое предложение всегда доверять .NET GC. Любые проблемы с памятью, проверьте счетчик производительности ".NET Memory" и диагностируйте мой собственный код.


Не уверен, что это лучшая практика...

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


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

ИМХО, это похоже на высказывание " если вы можете доказать, что ваша программа никогда не будет иметь никаких ошибок в будущем, то вперед..."

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