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

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

Ответ 1

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

В соответствии с MSDN:

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

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

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

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

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

Ответ 2

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

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

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

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

Ответ 3

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

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

Однако в некоторых процедурах пакетного типа вы знаете больше GC. Например. рассмотрите приложение, которое.

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

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

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

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

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

См. также " Rico Mariani Performance Tidbits"

Ответ 4

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

Ответ 5

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

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

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

Ответ 6

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

Ответ 7

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

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

Ответ 8

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

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

Ответ 9

Я думаю, что вы уже перечислили лучшую практику, и это НЕ использовать, если это НЕОБХОДИМО. Я бы настоятельно рекомендовал более подробно изучить ваш код, используя инструменты профилирования, если это необходимо, чтобы вначале ответить на эти вопросы.

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

Ответ 10

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

Проверьте счетчик производительности ".NET Memory", можете ли вы видеть, что проблема 1) действительно не проблема. Как правило, каждые 10 Gen0 GC запускают 1 Gen1 GC, и каждые 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.

Ответ 11

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

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

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

Ответ 12

Я хотел бы добавить, что: Вызов GC.Collect() (+ WaitForPendingFinalizers()) является частью истории. Как справедливо упоминается другими, GC.COllect() является недетерминированным набором и остается на усмотрение самого GC (CLR). Даже если вы добавите вызов WaitForPendingFinalizers, это может быть не детерминированным. Возьмите код из этой ссылки msdn и запустите код с итерацией цикла объекта как 1 или 2. Вы найдете то, что не детерминированные средства (набор точка останова в деструкторе объекта). Точно так же деструктор не вызывается, когда Wait..() имеет только 1 (или 2) затяжные объекты. [Citation 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(). Это сущность!:)

Ответ 13

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

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

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

Ответ 14

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

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

Ответ 15

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

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

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