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

У меня есть другой активный вопрос ЗДЕСЬ относительно некоторых безнадежных проблем с памятью, которые могут включать фрагментацию LOH среди, возможно, других неизвестных.

Какой у меня сейчас вопрос, каков приемлемый способ делать вещи? Если мое приложение должно быть выполнено в Visual C # и должно иметь дело с большими массивами до мелодии int [4000000], как я могу not быть обреченным из-за отказа сборщика мусора работать с LOH?

Казалось бы, я вынужден делать любые большие массивы глобальными и никогда не использовать слово "new" вокруг любого из них. Итак, у меня остались неблагодарные глобальные массивы с переменными «maxindex» вместо аккуратных по размеру массивов, которые передаются функциями.

Мне всегда говорили, что это плохая практика. Какая альтернатива есть?

Есть ли какая-то функция на мелодию System.GC.CollectLOH("Seriously")? Есть ли какой-нибудь способ передать сборщик мусора на что-то другое, кроме System.GC?

В любом случае, каковы общепринятые правила работы с большими (> 85Кб) переменными?

Ответы [ 5 ]

26 голосов
/ 08 марта 2010

Во-первых, сборщик мусора действительно собирает LOH, поэтому не стоит сразу пугаться его превосходства. LOH собирают, когда собирают поколение 2.

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

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

Начиная с .NET 4.5.1, LOH можно сжать, см. Свойство GCSettings.LargeObjectHeapCompactionMode .

Стратегии, позволяющие избежать фрагментации LOH:

  • Избегайте создания больших объектов, которые торчат вокруг. По сути, это просто означает большие массивы или объекты, которые обертывают большие массивы (такие как MemoryStream, который оборачивает байтовый массив), поскольку ничто иное не является настолько большим (компоненты сложных объектов хранятся отдельно в куче, поэтому редко бывают очень большими). Также следите за большими словарями и списками, так как они используют массив внутри.
  • Остерегайтесь двойных массивов - пороговое значение для этих входов в LOH намного, намного меньше - я не могу вспомнить точную цифру, но ее всего несколько тысяч.
  • Если вам нужен MemoryStream, подумайте над созданием фрагментированной версии, которая использует несколько меньших массивов, а не один огромный массив. Вы также можете создать пользовательскую версию IList и IDictionary, которые используют чанкинг, чтобы избежать попадания материала в LOH.
  • Избегайте очень длинных вызовов Remoting, поскольку Remoting интенсивно использует MemoryStreams, которые могут фрагментировать LOH во время продолжительности вызова.
  • Остерегайтесь интернирования строк - по какой-то причине они хранятся в виде страниц на LOH и могут вызвать серьезную фрагментацию, если ваше приложение продолжает сталкиваться с новыми строками для интернирования, т.е. избегать использования string.Intern, если набор строк не известен быть конечным, и полный набор встречается на раннем этапе жизни приложения. (См. мой предыдущий вопрос .)
  • Используйте Son of Strike, чтобы увидеть, что именно использует память LOH. Снова смотрите в этом вопросе , чтобы узнать, как это сделать.
  • Рассмотрим объединение больших массивов .

Редактировать: порог LOH для двойных массивов выглядит как 8k.

8 голосов
/ 08 апреля 2015

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

https://msdn.microsoft.com/en-us/library/xe0c2357(v=vs.110).aspx

"Начиная с .NET Framework 4.5.1, вы можете сжать кучу больших объектов (LOH), установив для свойства GCSettings.LargeObjectHeapCompactionMode значение GCLargeObjectHeapCompactionMode.CompactOnce перед вызовом метода Collect, как показано в следующем примере." *

GCSettings можно найти в пространстве имен System.Runtime

GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect(); 
7 голосов
/ 08 марта 2010

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

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

6 голосов
/ 08 марта 2010

Вы ошибаетесь. Вам НЕ нужно иметь размер массива 4000000, и вам определенно не нужно вызывать сборщик мусора.

  • Напишите свою собственную реализацию IList. Нравится "PagedList"
  • Хранить элементы в массивах из 65536 элементов.
  • Создать массив массивов для хранения страниц.

Это позволяет вам получить доступ практически ко всем вашим элементам только ОДНОМ перенаправлении. И поскольку отдельные массивы меньше, фрагментация не является проблемой ...

... если это ... тогда ПРОЧИТАЙТЕ страницы. Не выбрасывайте их на утилизацию, поместите их в статический «PageList» и сначала вытяните их оттуда. Все это может быть прозрачно сделано в вашем классе.

Действительно хорошо, что этот список довольно динамичен в использовании памяти. Вы можете изменить размер массива держателей (перенаправитель). Даже если это не так, она составляет около 512 КБд на страницу только.

Массивы второго уровня имеют в основном 64 КБ на байт - что составляет 8 байт для класса (512 КБ на страницу, 256 КБ на 32-битной) или 64 КБ на каждый структурный байт.

Технически:

Turn ИНТ [] в ИНТ [] []

Решите, лучше ли 32 или 64 бит, как вы хотите;) Оба преимущества и недостатки.

Работа с ОДНЫМ большим массивом, подобным этому, неприемлема в любом языке - если вам это нужно, то ... в основном ... выделяйте при запуске программы и никогда не создавайте заново. Единственное решение.

0 голосов
/ 16 мая 2013

Добавляю уточнение к ответу выше, с точки зрения того, как проблема может возникнуть. Фрагментация LOH зависит не только от долгоживущих объектов, но если у вас есть ситуация, когда существует несколько потоков, и каждый из них создает большие списки, идущие на LOH, то вы можете иметь ситуацию, когда первый поток необходимо увеличить свой список, но следующий непрерывный бит памяти уже занят списком из второго потока, поэтому среда выполнения будет выделять новую память для первого списка потоков, оставляя за собой довольно большую дыру. Это то, что происходит в настоящее время в одном проекте, который я унаследовал, и поэтому, несмотря на то, что LOH составляет приблизительно 4,5 МБ, среда выполнения имеет в общей сложности 117 МБ свободной памяти, но самый большой сегмент свободной памяти составляет 28 МБ.

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

Полезная ссылка: https://www.simple -talk.com / dotnet / .net-framework / куча опасных объектов /

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

...