Уменьшение использования памяти приложениями .NET?

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

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

Скомпилированный в режиме выпуска для 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 # немного проще? Я пишу программу, которая большую часть времени предназначена для работы в фоновом режиме. Я уже занимаюсь любым пользовательским интерфейсом в отдельном домене приложения, что означает, что пользовательский интерфейс может быть безопасно выгруженным, но занимать 10 МБ, когда он просто находится в фоновом режиме, кажется чрезмерным.

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


person Robert Fraser    schedule 27.08.2009    source источник
comment
Зачем тебе это нужно? Частный рабочий набор довольно низкий. Современные ОС будут выгружать страницу на диск, если память не нужна. На дворе 2009 год. Если вы не создаете что-то на встраиваемых системах, вам не нужно беспокоиться о 10 МБ.   -  person mmx    schedule 27.08.2009
comment
Прекратите использовать .NET, и вы сможете создавать небольшие программы. Чтобы загрузить платформу .NET, в память необходимо загрузить множество больших DLL.   -  person Adam Sills    schedule 28.08.2009
comment
Цены на память падают экспоненциально (да, сейчас вы можете заказать домашний компьютер Dell с 24 ГБ ОЗУ). Если ваше приложение не использует ›500 МБ, оптимизация не требуется.   -  person Alex    schedule 28.08.2009
comment
Удалите все неиспользуемые ссылки в вашем приложении и импорте кода (VB) или с помощью (C #), чтобы .NET загружал минимально возможный BCL.   -  person Jamie Clayton    schedule 05.01.2014
comment
Обратите внимание, что новые выпуски .net включают более агрессивную сборку мусора. См. msdn.microsoft.com/en-us/ библиотека / ee787088 (v = vs.110) .aspx   -  person Jamie Clayton    schedule 05.01.2014
comment
@LeakyCode Мне очень неприятно, что современные программисты думают так, вы должны заботиться об использовании памяти вашим приложением. Я могу сказать, что большинство современных приложений, написанных в основном на java или C #, довольно неэффективны, когда дело доходит до управления ресурсами, и благодаря этому в 2014 году мы можем запускать столько приложений, сколько мы могли в 1998 году, на win95 и 64 МБ оперативной памяти. ... всего 1 экземпляр браузера теперь ест 2 ГБ оперативной памяти, а простая IDE около 1 ГБ. Баран дешевый, но это не значит, что вы должны его тратить.   -  person Petr    schedule 03.06.2014
comment
@Petr Тебе стоит позаботиться об управлении ресурсами. Время программиста - тоже ресурс. Пожалуйста, не преувеличивайте, тратя 10 МБ на 2 ГБ.   -  person mmx    schedule 04.06.2014


Ответы (9)


  1. Возможно, вы захотите проверить вопрос о переполнении стека Объем памяти .NET EXE .
  2. Сообщение в блоге MSDN Рабочий набор! = фактический объем памяти предназначен для демистификации рабочего набора, памяти процесса и того, как выполнять точные вычисления на основе вашего общего объема памяти. Потребление RAM.

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

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

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

person John Rudy    schedule 27.08.2009

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

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

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

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

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

person Brian Rasmussen    schedule 27.08.2009
comment
IOW, в .NET работают те же методы, которые работают для уменьшения размера нативного кода? - person Robert Fraser; 28.08.2009
comment
Я предполагаю, что есть некоторое совпадение, но с собственным кодом у вас есть больше возможностей, когда дело доходит до использования памяти. - person Brian Rasmussen; 28.08.2009

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

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

person JaredPar    schedule 27.08.2009

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

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

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

person Donut    schedule 27.08.2009

Возможно, вы захотите посмотреть на использование памяти «настоящим» приложением.

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

person Eric Petroelje    schedule 27.08.2009

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

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

  2. Обучите свое приложение с помощью MPGO сокращение использования памяти а потом НГЕН это.

person Feng Yuan    schedule 20.07.2012

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

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

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

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

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

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

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

person Robert Giesecke    schedule 28.10.2009

Отвечая на общий вопрос в заголовке, а не на конкретный вопрос:

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

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

person Peter Mortensen    schedule 27.08.2009

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

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

person Stuart Welch    schedule 18.02.2015