Сокращение использования памяти приложений .NET? - PullRequest
106 голосов
/ 27 августа 2009

Какие советы помогут сократить использование памяти приложениями .NET?Рассмотрим следующую простую программу на C #.

class Program
{
    static void Main(string[] args)
    {
        Console.ReadLine();
    }
}

Скомпилированная в режиме release для x64 и работающая вне Visual Studio, диспетчер задач сообщает следующее:

Working Set:          9364k
Private Working Set:  2500k
Commit Size:         17480k

Немного лучше, если он скомпилирован только для x86 :

Working Set:          5888k
Private Working Set:  1280k
Commit Size:          7012k

Затем я попробовал следующую программу, которая делает то же самое, но пытается обрезать размер процесса послеинициализация во время выполнения:

class Program
{
    static void Main(string[] args)
    {
        minimizeMemory();
        Console.ReadLine();
    }

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

Результаты x86 Выпуск вне Visual Studio:

Working Set:          2300k
Private Working Set:   964k
Commit Size:          8408k

Что немного лучше, но этовсе еще кажется чрезмерным для такой простой программы.Есть ли какие-то хитрости, чтобы сделать процесс в C # более скудным?Я пишу программу, которая предназначена для работы в фоновом режиме большую часть времени.Я уже делаю какие-либо пользовательские интерфейсы в отдельном Application Domain , что означает, что пользовательский интерфейс может быть безопасно выгружен, но занимает 10 МБ, когда он просто находится в фоновом режиме, кажется чрезмерным.

PS Относительно того, почему меня это волнует --- (Power) пользователи склонны беспокоиться об этих вещах.Даже если это почти не влияет на производительность, пользователи, обладающие техническими знаниями (моя целевая аудитория), склонны впадать в шутки по поводу использования памяти фоновыми приложениями.Даже я волнуюсь, когда вижу Adobe Updater, занимающий 11 МБ памяти, и чувствую себя успокоенным от успокаивающего прикосновения Foobar2000, которое может занять менее 6 МБ даже во время игры.Я знаю, что в современных операционных системах этот материал технически не имеет большого значения, но это не значит, что он не влияет на восприятие.

Ответы [ 9 ]

44 голосов
/ 27 августа 2009

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

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

Если вы хотите сохранить небольшой размер, вам придется подумать об использовании памяти. Вот пара идей:

  • Уменьшите количество объектов и постарайтесь не удерживать ни один экземпляр дольше, чем требуется.
  • Имейте в виду, что List<T> и аналогичные типы удваивают емкость при необходимости, так как они могут привести к до 50% отходов.
  • Вы можете рассмотреть возможность использования типов значений над ссылочными типами, чтобы увеличить объем памяти в стеке, но имейте в виду, что пространство стека по умолчанию составляет всего 1 МБ.
  • Избегайте объектов размером более 85000 байт, так как они попадут в LOH, который не уплотнен и поэтому может легко фрагментироваться.

Это, вероятно, не исчерпывающий список, а всего лишь пара идей.

32 голосов
/ 28 августа 2009
  1. Возможно, вы захотите проверить вопрос переполнения стека .NET Footprint памяти .
  2. Сообщение в блоге MSDN Рабочий набор! = Фактический объем памяти - все о демистификации рабочего набора, памяти процесса и о том, как выполнять точные вычисления общего потребления в оперативной памяти.

Я не скажу, что вы должны игнорировать объем памяти вашего приложения - очевидно, что его желательно уменьшить и сделать его более эффективным. Тем не менее, вы должны учитывать ваши реальные потребности.

Если вы пишете стандартные клиентские приложения Windows Forms и WPF, предназначенные для запуска на ПК отдельного пользователя и, вероятно, являющиеся основным приложением, в котором работает пользователь, вы можете избежать лишних вопросов относительно распределения памяти. , (Пока все это освобождается.)

Однако, чтобы обратиться к некоторым людям, которые говорят, что не стоит об этом беспокоиться: если вы пишете приложение Windows Forms, которое будет работать в среде служб терминалов, на общем сервере, возможно, будет использоваться 10, 20 или более пользователей. тогда да, вы обязательно должны учитывать использование памяти. И вам нужно быть бдительным. Наилучший способ решить эту проблему - с помощью правильного проектирования структуры данных и следования передовым практикам в отношении того, когда и что вы выделяете.

16 голосов
/ 27 августа 2009

В данном случае необходимо учитывать стоимость памяти CLR. CLR загружается для каждого процесса .Net и, следовательно, учитывает факторы памяти. Для такой простой / маленькой программы стоимость CLR будет доминировать в вашей памяти.

Было бы гораздо более поучительно создать реальное приложение и оценить его стоимость по сравнению со стоимостью этой базовой программы.

7 голосов
/ 27 августа 2009

Никаких конкретных предложений как таковых нет, но вы можете взглянуть на CLR Profiler (бесплатная загрузка от Microsoft)
Установив его, взгляните на эту страницу с инструкциями .

Из инструкции:

This How To показывает, как использовать Инструмент CLR Profiler для исследования вашего распределение памяти приложения профиль. Вы можете использовать CLR Profiler для определить код, который вызывает память проблемы, такие как утечки памяти и чрезмерный или неэффективный мусор коллекция.

6 голосов
/ 27 августа 2009

Возможно, стоит взглянуть на использование памяти "настоящим" приложением.

Как и в Java, для среды выполнения существует определенный фиксированный объем служебных данных независимо от размера программы, но после этого потребление памяти будет намного более разумным.

4 голосов
/ 21 июля 2012

Есть еще способы уменьшить частный рабочий набор этой простой программы:

  1. NGEN ваше заявление. Это устраняет стоимость компиляции JIT из вашего процесса.

  2. Обучите ваше приложение, используя MPGO , уменьшив использование памяти и затем NGEN.

2 голосов
/ 28 октября 2009

Есть много способов уменьшить ваш след.

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

И этот код не может быть полностью разделен между экземплярами приложения. Даже NGEN'ed сборки не являются полностью статичными, у них все еще есть небольшие детали, которые требуют JITting.

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

Часто встречающийся пример: использование Datareader, загрузка содержимого в DataTable просто для записи его в файл XML. Вы можете легко столкнуться с OutOfMemoryException. OTOH, вы можете использовать XmlTextWriter и прокручивать Datareader, испуская XmlNodes при прокрутке курсора базы данных. Таким образом, у вас есть только текущая запись базы данных и ее вывод XML в памяти. Который никогда не (или вряд ли) получит более высокое поколение сборки мусора и, следовательно, может быть повторно использован.

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

C # имеет отличную функцию, называемую итераторы. Они позволяют вам выполнять потоковую передачу объектов путем прокрутки ввода и сохранять только текущий экземпляр, пока вы не получите следующий. Даже используя LINQ, вам не нужно хранить все это только потому, что вы хотите, чтобы оно было отфильтровано.

1 голос
/ 28 августа 2009

Решение общего вопроса в названии, а не конкретный вопрос:

Если вы используете COM-компонент, который возвращает много данных (скажем, большие 2xN массивы двойников) и только небольшая дробь тогда можно написать обёртку COM-компонент, который скрывает память от .NET и возвращает только те данные, которые нужен.

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

0 голосов
/ 18 февраля 2015

Я обнаружил, что использование API-интерфейсов SetProcessWorkingSetSize или EmptyWorkingSet для периодической принудительной принудительной загрузки страниц памяти на диск в длительном процессе может привести к эффективному исчезновению всей доступной физической памяти на машине до ее перезагрузки. У нас была .NET DLL, загруженная в собственный процесс, который использовал API-интерфейс EmptyWorkingSet (альтернатива использованию SetProcessWorkingSetSize), чтобы уменьшить рабочий набор после выполнения задачи, интенсивно использующей память. Я обнаружил, что через какое-то время от 1 дня до недели машина показывает 99% использования физической памяти в диспетчере задач, в то время как ни один из процессов не использует сколько-нибудь значительный объем памяти. Вскоре машина перестанет отвечать на запросы, требуя полной перезагрузки. На этих машинах было более двух десятков серверов Windows Server 2008 R2 и 2012 R2, работающих как на физическом, так и на виртуальном оборудовании.

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

...