Лучшая практика для принудительного сбора мусора в C # - PullRequest
114 голосов
/ 24 октября 2008

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

Ответы [ 15 ]

4 голосов
/ 17 марта 2014

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

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

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

Примечание : Если вы уже попробовали приведенный выше пример из 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 (). Это суть! :)

4 голосов
/ 24 октября 2008

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

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

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

2 голосов
/ 02 ноября 2008

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

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

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

1 голос
/ 10 января 2012

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

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

0 голосов
/ 02 ноября 2008

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

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

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

...