Когда можно позвонить в GC.Collect? - PullRequest
154 голосов
/ 25 января 2009

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

Я могу вспомнить только несколько очень конкретных случаев, когда имеет смысл форсировать сборку мусора.

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

Есть ли другие случаи, когда допустимо звонить GC.Collect?

Ответы [ 22 ]

4 голосов
/ 11 июля 2017

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

При программировании с использованием Interop, каждый COM-объект должен быть освобожден вручную, обычно с использованием Marshal.ReleseComObject (). Кроме того, вызов Garbage Collection вручную может помочь немного «очиститься». Вызов следующего кода, когда вы закончите с объектами Interop, кажется, очень помогает:

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

По моему личному опыту, использование комбинации ReleaseComObject и ручного вызова сборки мусора значительно уменьшает использование памяти продуктами Office, в частности Excel.

3 голосов
/ 04 июля 2013

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

Например, у меня есть несколько тестов, таких как:

WeakReference w = CodeThatShouldNotMemoryLeak();
Assert.IsTrue(w.IsAlive);
GC.Collect();
GC.WaitForPendingFinalizers();
Assert.IsFalse(w.IsAlive);

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

2 голосов
/ 26 июня 2013

Я все еще не уверен в этом. Я работаю с 7 лет на сервере приложений. Наши большие установки используют 24 ГБ оперативной памяти. Его многопоточный и ВСЕ вызовы для GC.Collect () столкнулись с действительно ужасными проблемами с производительностью.

Многие сторонние Компоненты использовали GC.Collect (), когда они думали, что было бы разумно сделать это прямо сейчас. Таким образом, простая группа Excel-отчетов блокировала сервер приложений для всех потоков несколько раз в минуту.

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

Но я также запускаю Серверы на Win32, и здесь я начал активно использовать GC.Collect () после получения исключения OutOfMemoryException.

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

Одна вещь, которая меня интересует, это само исключение OOM ... Если бы я написал .Net Framework, и я не могу выделить блок памяти, я бы использовал GC.Collect (), дефрагментировать память (??), попробуйте еще раз, и если я все еще не могу найти свободный блок памяти, тогда я бы бросил OOM-исключение.

Или, по крайней мере, сделайте это поведение настраиваемым параметром из-за недостатков производительности, связанных с GC.Collect.

Теперь в моем приложении много такого кода, чтобы «решить» проблему:

public static TResult ExecuteOOMAware<T1, T2, TResult>(Func<T1,T2 ,TResult> func, T1 a1, T2 a2)
{

    int oomCounter = 0;
    int maxOOMRetries = 10;
    do
    {
        try
        {
            return func(a1, a2);
        }
        catch (OutOfMemoryException)
        {
            oomCounter++;
            if (maxOOMRetries > 10)
            {
                throw;
            }
            else
            {
                Log.Info("OutOfMemory-Exception caught, Trying to fix. Counter: " + oomCounter.ToString());
                System.Threading.Thread.Sleep(TimeSpan.FromSeconds(oomCounter * 10));
                GC.Collect();
            }
        }
    } while (oomCounter < maxOOMRetries);

    // never gets hitted.
    return default(TResult);
}

(Обратите внимание, что поведение Thread.Sleep () действительно специфично для приложения, поскольку мы запускаем службу кэширования ORM, и службе требуется некоторое время для освобождения всех кэшированных объектов, если объем оперативной памяти превышает некоторые предопределенные значения. он ждет несколько секунд в первый раз и увеличивает время ожидания при каждом появлении OOM.)

2 голосов
/ 19 августа 2011

В некоторых ситуациях это лучше, чем потом сожалеть.

Вот одна ситуация.

Можно написать неуправляемую DLL в C # с помощью переписывания IL (поскольку существуют ситуации, когда это необходимо).

Теперь предположим, например, что DLL создает массив байтов на уровне класса - потому что многим экспортируемым функциям необходим доступ к таким. Что происходит, когда DLL выгружается? В этот момент автоматически вызывается сборщик мусора? Я не знаю, но, будучи неуправляемой DLL, вполне возможно, что GC не вызывается. И это было бы большой проблемой, если бы его не называли. Когда DLL выгружается, сборщик мусора тоже будет - так кто же будет отвечать за сбор любого возможного мусора и как они это сделают? Лучше использовать сборщик мусора в C #. Иметь функцию очистки (доступную для клиента DLL), в которой переменные уровня класса имеют значение null, а сборщик мусора называется.

Лучше, чем потом сожалеть.

2 голосов
/ 25 января 2009

Короткий ответ: никогда!

2 голосов
/ 25 января 2009

Запись Скотта Холдена в блоге о том, когда (а когда нет) вызывать GC.Collect относится к .NET Compact Framework , но правила обычно применяются ко всей управляемой разработке .

2 голосов
/ 05 августа 2011
using(var stream = new MemoryStream())
{
   bitmap.Save(stream, ImageFormat.Png);
   techObject.Last().Image = Image.FromStream(stream);
   bitmap.Dispose();

   // Without this code, I had an OutOfMemory exception.
   GC.Collect();
   GC.WaitForPendingFinalizers();
   //
}
1 голос
/ 23 февраля 2014

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

        public void ClearFrame(ulong timeStamp)
    {
        if (RecordSet.Count <= 0) return;
        if (Limit == false)
        {
            var seconds = (timeStamp - RecordSet[0].TimeStamp)/1000;
            if (seconds <= _preFramesTime) return;
            Limit = true;
            do
            {
                RecordSet.Remove(RecordSet[0]);
            } while (((timeStamp - RecordSet[0].TimeStamp) / 1000) > _preFramesTime);
        }
        else
        {
            RecordSet.Remove(RecordSet[0]);

        }
        GC.Collect(); // AVOID
    }

РЕЗУЛЬТАТ ИСПЫТАНИЯ: ИСПОЛЬЗОВАНИЕ ЦП 12%

При переходе на это:

        public void ClearFrame(ulong timeStamp)
    {
        if (RecordSet.Count <= 0) return;
        if (Limit == false)
        {
            var seconds = (timeStamp - RecordSet[0].TimeStamp)/1000;
            if (seconds <= _preFramesTime) return;
            Limit = true;
            do
            {
                RecordSet[0].Dispose(); //  Bitmap destroyed!
                RecordSet.Remove(RecordSet[0]);
            } while (((timeStamp - RecordSet[0].TimeStamp) / 1000) > _preFramesTime);
        }
        else
        {
            RecordSet[0].Dispose(); //  Bitmap destroyed!
            RecordSet.Remove(RecordSet[0]);

        }
        //GC.Collect();
    }

РЕЗУЛЬТАТ ИСПЫТАНИЯ: ИСПОЛЬЗОВАНИЕ ЦП 2-3%

0 голосов
/ 28 декабря 2017

Так как есть куча малых объектов (SOH) и куча больших объектов (LOH)

Мы можем вызвать GC.Collect (), чтобы очистить объект де-ссылки в SOP и переместить живой объект в следующее поколение.

В .net4.5 мы также можем уплотнить LOH, используя режим большого объекта сравнения

0 голосов
/ 25 января 2009

Это не имеет отношения к вопросу, но для XSLT-преобразований в .NET (XSLCompiledTranform) у вас может не быть выбора. Другим кандидатом является элемент управления MSHTML.

...