Когда допустимо вызывать GC.Collect?

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

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

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

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

Ответ 1

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

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

Ответ 2

Я использую GC.Collect только при написании буровых установок для тестирования производительности/профилировщика; то есть у меня есть два (или более) блока кода для проверки - что-то вроде:

GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
TestA(); // may allocate lots of transient objects
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
TestB(); // may allocate lots of transient objects
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
...

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

Классическим примером может быть простая консоль exe (сортировка метода Main - достаточно, чтобы ее можно было разместить здесь, например), которая показывает разницу между объединенной конкатенацией строк и StringBuilder.

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

Во время производственного кода? Я еще не использовал его; -p

Ответ 3

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

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

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

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

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

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

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

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

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

Ответ 4

Один случай - это когда вы пытаетесь использовать код unit test, который использует WeakReference.

Ответ 5

В больших системах 24/7 или 24/6 - системах, которые реагируют на сообщения, запросы RPC или опрашивают базу данных или процесс непрерывно - полезно иметь способ идентифицировать утечки памяти. Для этого я склонен добавить механизм для приложения, чтобы временно приостановить любую обработку, а затем выполнить полную сборку мусора. Это ставит систему в состояние покоя, когда оставшаяся память представляет собой либо законно долгоживущую память (кеши, конфигурация и т.д.), Либо же "утечка" (объекты, которые не ожидаются или желательны для корневых, но на самом деле).

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

Чтобы убедиться, что вы получаете весь мусор, вам нужно выполнить две коллекции:

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

Поскольку первый набор вызовет завершение любых объектов с финализаторами (но на самом деле не собирать мусор этих объектов). Второй GC будет мусором собирать эти завершенные объекты.

Ответ 6

Взгляните на эту статью Рико Мариани. Он дает два правила, когда вызывать GC.Collect(правило 1: "Не делать" ):

Когда вызывать GC.Collect()

Ответ 7

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

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

Однако в большинстве случаев GC достаточно умен, чтобы сделать это в любом случае.

Ответ 8

В качестве решения для фрагментации памяти. Я получал исключения из памяти при записи большого количества данных в поток памяти (чтение из сетевого потока). Данные были записаны в 8K кусках. После достижения 128M было исключение, хотя было много доступной памяти (но было фрагментировано). Вызов GC.Collect() решил проблему. Я смог обработать 1G после исправления.

Ответ 9

В вашем примере, я думаю, что вызов GC.Collect не является проблемой, а проблема с дизайном.

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

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

Сказав это, у Рико Мариани есть отличный пост в блоге по этому вопросу, который можно найти здесь:

http://blogs.msdn.com/ricom/archive/2004/11/29/271829.aspx

Ответ 10

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

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

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

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

Ответ 12

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

Ответ 13

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();
   //
}

Ответ 14

Есть ситуации, когда лучше безопасно, чем жаль.

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

В С# можно создать неуправляемую DLL-версию с использованием перезаписи IL (потому что есть ситуации, когда это необходимо).

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

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

Ответ 15

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

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

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

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

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

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

Или, по крайней мере, сделать это поведение настраиваемым, из-за недостатков производительности с помощью 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.)

Ответ 16

Вы должны стараться избегать использования 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%

Ответ 17

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

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

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

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

Ответ 18

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

Ответ 20

Одна из веских причин для вызова GC - на небольших ARM-компьютерах с небольшой памятью, например, с малиновой PI (работает с моно). Если нераспределенные фрагменты памяти используют слишком большую часть ОЗУ системы, то ОС Linux может стать нестабильной. У меня есть приложение, где я должен каждый раз вызывать GC (!), Чтобы избавиться от проблем с переполнением памяти.

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

Ответ 21

Я выполнял некоторые тесты производительности в массиве и список:

private static int count = 100000000;
private static List<int> GetSomeNumbers_List_int()
{
    var lstNumbers = new List<int>();
    for(var i = 1; i <= count; i++)
    {
        lstNumbers.Add(i);
    }
    return lstNumbers;
}
private static int[] GetSomeNumbers_Array()
{
    var lstNumbers = new int[count];
    for (var i = 1; i <= count; i++)
    {
        lstNumbers[i-1] = i + 1;
    }
    return lstNumbers;
}
private static int[] GetSomeNumbers_Enumerable_Range()
{
    return  Enumerable.Range(1, count).ToArray();
}

static void performance_100_Million()
{
    var sw = new Stopwatch();

    sw.Start();
    var numbers1 = GetSomeNumbers_List_int();
    sw.Stop();
    //numbers1 = null;
    //GC.Collect();
    Console.WriteLine(String.Format("\"List<int>\" took {0} milliseconds", sw.ElapsedMilliseconds));

    sw.Reset();
    sw.Start();
    var numbers2 = GetSomeNumbers_Array();
    sw.Stop();
    //numbers2 = null;
    //GC.Collect();
    Console.WriteLine(String.Format("\"int[]\" took {0} milliseconds", sw.ElapsedMilliseconds));

    sw.Reset();
    sw.Start();
//getting System.OutOfMemoryException in GetSomeNumbers_Enumerable_Range method
    var numbers3 = GetSomeNumbers_Enumerable_Range();
    sw.Stop();
    //numbers3 = null;
    //GC.Collect();

    Console.WriteLine(String.Format("\"int[]\" Enumerable.Range took {0} milliseconds", sw.ElapsedMilliseconds));
}

и я получил OutOfMemoryException в методе GetSomeNumbers_Enumerable_Range, единственным обходным решением является освобождение памяти:

numbers = null;
GC.Collect();