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

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

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

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

Мне всегда говорили, что это было плохой практикой. Что альтернатива там?

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

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

12
задан Community 23 May 2017 в 12:34
поделиться

3 ответа

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

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

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

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

Стратегии предотвращения фрагментации LOH:

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

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

26
ответ дан 2 December 2019 в 03:16
поделиться

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

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

7
ответ дан 2 December 2019 в 03:16
поделиться

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

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

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

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

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

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

Технически:

Превратите int [] в int [] []

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

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

6
ответ дан 2 December 2019 в 03:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: