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

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

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

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

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

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

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

Ответ 1

Во-первых, сборщик мусора собирает 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 для двойных массивов, кажется, 8 КБ.

Ответ 2

Это старый вопрос, но я считаю, что не нужно обновлять ответы с изменениями, внесенными в .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(); 

Ответ 3

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

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

Ответ 4

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

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

Это позволяет вам получить доступ в основном к всем вашим элементам только с ПЕРЕЧИСЛЕНИЕМ ONE. И, поскольку отдельные массивы меньше, фрагментация не является проблемой...

... если это... затем страницы REUSE. Не выбрасывайте их на место, ставьте их на статичный "лист лист" и вытаскивайте их оттуда первым. Все это может быть прозрачно выполнено в вашем классе.

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

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

Технически:

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

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

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

Ответ 5

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

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

Полезная ссылка: https://www.simple-talk.com/dotnet/.net-framework/the-dangers-of-the-large-object-heap/

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