Сборка мусора .NET - На что это влияет? - PullRequest
6 голосов
/ 08 января 2010

У нас есть ситуация, когда мы рассматриваем принудительную сборку мусора на сервере с очень низким объемом оперативной памяти (в среднем 3,6 / 4 ГБ). Нет, к сожалению, это не вариант обновления этого сервера.

Один из наших сервисных процессов (написанных косвенно на C ++ (не спрашивайте ...)) работает с компонентами .NET и затем спит в течение 10 минут. Когда этот сервис находится в спящем режиме, он часто зависает на 600 МБ ОЗУ, которое может использоваться другими процессами. Похоже, это связано с тем, что трассировка WSE включена для отладки. Я могу наблюдать, как он просыпается, и GC на следующей итерации при первом вызове COM в .NET - однако тогда процесс выполняет некоторую работу, и к тому времени, когда он заходит в спящий режим, использование оперативной памяти возвращается к 600 МБ ... можно увидеть, куда это идет ...

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

Ответы [ 5 ]

10 голосов
/ 08 января 2010

Это повлияет только на процесс, который вызывает GC.

8 голосов
/ 08 января 2010

Это просто влияет на процесс, в котором он вызывается (но повлияет на все Домены приложений в процессе). Однако следует помнить, что это не освободит память для ОС. Если вы хотите освободить память для ОС, способ сделать это:

private static void minimizeMemory()
{
    GC.Collect(GC.MaxGeneration);
    GC.WaitForPendingFinalizers();
    SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle,
        (UIntPtr) 0xFFFFFFFF, (UIntPtr) 0xFFFFFFFF);
}

[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetProcessWorkingSetSize(IntPtr process,
    UIntPtr minimumWorkingSetSize, UIntPtr maximumWorkingSetSize);

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

Я знаю это по опыту - я задал этот вопрос некоторое время назад и даже придумал класс на основе таймера для вызова описанного выше метода после выхода из операции с большим объемом памяти (запускается каждые 5 минут ). После этого рабочий набор процесса значительно уменьшится - путем его замены на диск. В течение следующих нескольких секунд он считывал наиболее часто используемые части, а через пару минут (когда задача запускалась снова) снова выполнял кучу выделений. Время (даже профилирование) процесса показало, что при сборе во время выделения виртуальной машины из ОС для выполнения задачи наблюдалось замедление примерно на 0,2 с (!).

5 голосов
/ 08 января 2010

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

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

Добавьте в app.config следующее:

<Configuration>
    <runtime>
        <gcServer enabled=“true“ />
    </runtime>
</Configuration> 

Linky to больше информации

4 голосов
/ 08 января 2010

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

Отображение физической памяти в адресное пространство процесса - сложная вещь. Сборка мусора в .NET-процессе может увеличить свободное пространство в управляемой куче, но это не обязательно приведет к уменьшению размера рабочего набора (памяти, которую ваш процесс Win32 выделяет из ОС). Даже если бы вы могли заставить такую ​​схему работать - она ​​была бы в лучшем случае хрупкой и не могла бы работать при любых обстоятельствах.

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

2 голосов
/ 08 января 2010

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

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

Есть ли места, где вы можете использовать оператор using для вызова метода Dispose? Ничто из этого не поможет с физической памятью, но поможет с такими вещами, как HANDLE для объектов Windows и т. Д.

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