Использование свободной памяти .NET (как предотвратить перераспределение / освобождение памяти для ОС) - PullRequest
16 голосов
/ 21 марта 2012

В настоящее время я работаю над веб-сайтом, который широко использует кэшированные данные, чтобы избежать обходов.При запуске мы получаем «большой» график (сотни тысяч различных типов объектов).Эти объекты извлекаются через WCF и десериализуются (мы используем буферы протокола для сериализации). Я использую профилировщик памяти redgate для устранения проблем с памятью (кажется, что память не соответствует тому, сколько памяти нам нужно «после», когда мы закончим)Инициализация и создание этого отчета

Global Report

Теперь мы можем получить из этого отчета следующее:

1) Большая часть выделенной памяти .NET свободна(возможно, он был правильно выделен во время десериализации, но теперь, когда он свободен, я бы хотел, чтобы он вернулся в ОС)

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

3) Я понятия не имею, почему пространство фрагментировано, потому что, когда я смотрю накуча больших объектов, всего 30 экземпляров, 15 объектов [] напрямую подключены к ГХ и совершенно не связаны со мной, 1 являетсямассив char также подключен непосредственно к GC Heap, остальные 15 принадлежат мне, но не являются причиной этого, поскольку я получаю тот же отчет, если закомментирую их в коде.

Поэтому мой вопрос: что я могусделать, чтобы пойти дальше с этим?Я не совсем уверен, что искать в отладке / инструментах, так как кажется, что моя память фрагментирована, но не мной, и .net выделяет огромное количество свободного места, которое я не могу освободить.

Также, пожалуйста, убедитесь, что вы хорошо поняли вопрос, прежде чем ответить, я не ищу способ освободить память в .net (GC.Collect), но чтобы освободить память, которая уже свободна в .net, для системы кака также для дефрагментации упомянутой памяти.

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

Спасибо за вашу помощь!

Несколько замечаний, которые я забыл: 1) Проект представляет собой веб-сайт .net 2.0, я получаю те же результатыв пуле .net 4, то есть, если я запустил его в пуле .net 4, преобразовал в .net 4 и перекомпилировал.

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

3) И это яЭто, вероятно, очень важно, я вообще не получаю эти проблемы на сервере webdev, только в IIS, в webdev потребление памяти довольно близко к моему фактическому потреблению (ну, больше, но не в 5-10 раз больше!)

Ответы [ 6 ]

7 голосов
/ 22 марта 2012

Объекты, размещенные в куче больших объектов (объекты> = 85 000 байт, обычно массивы), не сжимаются сборщиком мусора.Microsoft решила, что стоимость перемещения этих объектов будет слишком высокой.

Рекомендуется по возможности повторно использовать крупные объекты, чтобы избежать фрагментации в управляемой куче и пространстве виртуальной машины.*

http://msdn.microsoft.com/en-us/magazine/cc534993.aspx

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

4 голосов
/ 21 марта 2012

Я знаю, что это не тот ответ, который вы хотели бы услышать, но вы не можете принудительно вернуть память обратно в ОС. Однако по какой причине вы хотите это сделать? .NET освободит свою кучу обратно в ОС, когда у вас кончится физическая память. Но если имеется достаточное количество свободной физической памяти, .NET сохранит свою кучу, чтобы ускорить распределение объектов в будущем. Если вы действительно хотите заставить .NET выпустить свою кучу обратно в ОС, я полагаю, вы могли бы написать программу на C, которая просто выдает malloc до тех пор, пока не закончится память. Это должно заставить ОС сигнализировать .NET освободить свою неиспользуемую часть кучи.

Лучше, чтобы неиспользуемая память была восстановлена ​​для .NET, чтобы ваше приложение имело лучшую производительность выделения (так как среда выполнения знает, какая память свободна, а какая нет, выделение может просто использовать свободную память без необходимости системного вызова в ОС, чтобы получить больше памяти).

Сборщик мусора отвечает за дефрагментацию кучи. Время от времени (обычно во время выполнения сбора) он будет перемещать объекты вокруг кучи, если определит, что это необходимо сделать. (Вот почему C ++ / CLI имеет конструкцию pin_ptr для «закрепления» объектов).

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

Что касается вашего OutOfMemoryException, у меня нет хорошего ответа для. Обычно я подозреваю, что ваш старый граф объектов не собирается (какой-то объект содержит ссылку на него, «утечка памяти»). Но так как вы используете профилировщик, я не знаю тогда.

2 голосов
/ 01 марта 2017

Начиная с .NET 4.5.1, вы можете установить одноразовый флаг для сжатия LOH перед выполнением вызова сбора GC, т.е.

Runtime.GCSettings.LargeObjectHeapCompactionMode = System.Runtime.GCLargeObjectHeapCompactionMode.CompactOnce;GC.Collect ();// Это приведет к сжатию LOH (один раз).

2 голосов
/ 26 марта 2012

Некоторое тестирование и немного C ++ позже, я нашел причину, почему я получаю так много свободной памяти, потому что IIS создает экземпляр CLR через VM Hoarding (предоставление dll для его создания без VM Hoarding занимает столько же начальной памяти, но выпускает большую часть этого со временем, которое является поведением, которое я ожидаю).Так что это устраняет мою проблему с памятью, однако я все равно получаю около 100 МБ свободной памяти, несмотря на то, что, и я все еще думаю, что это происходит из-за фрагментации и фрагментов, высвобождаемых сразу, потому что профилировщик все еще сообщает о фрагментации памяти.Поэтому не помечайте мой собственный ответ как ответ в надежде, что кто-то сможет пролить свет на это или направить меня к инструментам, которые могут либо исправить это, либо помочь отладить основную причину.

1 голос
/ 22 марта 2012

Интересно, что на WebDevServer он работает иначе, чем на IIS ...

Возможно ли, что IIS использует серверный сборщик мусора, а сервер WebDev - сборщик мусора на рабочей станции? Метод сбора мусора может влиять на фрагментацию. Вероятно, он будет установлен в вашем файле aspnet.config. Смотри: http://support.microsoft.com/kb/911716

0 голосов
/ 18 июня 2012

Если вы не нашли свой ответ, я думаю, что следующие подсказки могут вам помочь:

Возвращаясь к основам: мы иногда забываем, что объекты могут быть явно освобождены, явно вызывает метод Dispose для объектов (поскольку вы не упомянули об этом, я полагаю, вы делаете "object = null" вместо инструкции).

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

Справка MSDN сообщает об этом методе:

... Нет преимущества в производительности при реализации утилизации метод для типов, которые используют только управляемые ресурсы (например, массивы) потому что они автоматически утилизируются сборщиком мусора. использование Метод Dispose в первую очередь для управляемых объектов, которые используют нативный ресурсы и COM-объекты, которые открыты для .NET Framework. ...

Поскольку он говорит, что « они автоматически возвращаются сборщиком мусора », мы можем сделать вывод, что при вызове метода «освобождает» ( Опять же, я пытаюсь дать вам только подсказки ).

Кроме того, я нашел эту интересную статью (полагаю ... я ее не прочитал ... полностью): Сборка мусора: автоматическое управление памятью в Microsoft .NET Framework (http://msdn.microsoft.com/en-us/magazine/bb985010.aspx), в котором говорится следующее Раздел « Принудительное очищение объекта »:

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

Возможно, ответ кроется в этой статье, если вы внимательно ее прочитаете или просто продолжаете расследование в этом направлении.

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