Большая фрагментация кучи объектов: у CLR есть какое-то решение?

Если приложение является таким, что оно должно выполнять много распределения/деления объектов большого размера ( > 85000 байт), в конечном итоге это приведет к фрагментации памяти, и приложение будет вызывать исключение из памяти.

Есть ли какое-либо решение этой проблемы или это ограничение управления памятью CLR?

Ответ 1

К сожалению, вся информация, которую я когда-либо видел, предлагает только самостоятельно управлять факторами риска: повторно использовать большие объекты, распределять их в начале, убедиться, что они имеют размеры, кратные друг другу, используют альтернативные структуры данных (списки, деревья) вместо массивов. Это просто дало мне еще одну идею создания нефрагментирующего списка, который вместо одного большого массива разбивается на более мелкие. Массивы/списки, по-видимому, являются наиболее частыми виновниками IME.

Вот статья журнала MSDN об этом: http://msdn.microsoft.com/en-us/magazine/cc534993.aspx, но в нем нет ничего полезного.

Ответ 2

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

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

Ответ 3

Дело в крупных объектах в сборщике мусора CLR заключается в том, что они управляются в другой куче. Сборщик мусора использует механизм под названием "Уплотнение", который представляет собой в основном фрагментацию и повторное соединение объектов в обычной куче. Дело в том, что, поскольку "уплотнение" больших объектов (копирование и переустановка их) является дорогостоящей процедурой, GC предоставляет для них другую кучу, которая никогда не уплотняется.

Обратите внимание, что распределение памяти смежно. Если вы назначаете Object # 1, а затем Object # 2, Object # 2 всегда будет помещен после Object # 1.

Вероятно, это то, что заставляет вас получать OutOfMemoryExceptions.

Я бы предложил посмотреть на шаблоны проектирования, такие как Flyweight, Lazy Initialization и Object Pool.

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

Ответ 4

Is there any solution to this problem or is it a limitation of CLR memory management?

Нет решения, кроме пересмотра вашего дизайна. И это не проблема CLR. Обратите внимание, что проблема неуправляемых приложений одинакова. Это объясняется тем, что слишком много памяти используется приложением одновременно и в сегментах, "невыгодных" в памяти. Если какой-то внешний виновник должен быть отмечен, тем не менее, я бы предпочел указать диспетчер памяти ОС, который (конечно) не уплотняет его адресное пространство vm.

CLR управляет свободными областями LOH в свободном списке. В большинстве случаев это лучшее, что можно сделать против фрагментации. Но так как для действительно больших объектов количество объектов на сегменте LOH уменьшается, мы в конечном итоге получаем только один объект на сегмент. И там, где эти объекты расположены в пространстве vm, полностью зависит от диспетчера памяти ОС. Это означает, что фрагментация в основном происходит на уровне ОС - не на CLR. Это часто наблюдаемый аспект фрагментации кучи, и это не .NET обвинять в этом. (Но это также верно, фрагментация также может возникать на управляемой стороне, как хорошо продемонстрировано в этой статье.)

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

Ответ 5

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

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

Ответ 6

Я видел в другом ответе, что LOH может уменьшаться в размере:

Большие массивы и фрагментация LOH. Что такое принятое соглашение?

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

Кроме того, вы можете сделать свою программу с расширенной памятью до 3 ГБ на 32-битной системе и до 4 ГБ на 64-битной системе. Просто добавьте флаг /LARGEADDRESSAWARE в свой компоновщик или событие post post:

вызов "$ (DevEnvDir)..\tools\vsvars32.bat" editbin/LARGEADDRESSAWARE "$ (TargetPath)"

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