Подтвердить что ты не робот

Принудительная сборка мусора массивов, С#

У меня проблема, когда пара 3-мерных массивов выделяет огромное количество памяти, и программе иногда требуется заменить их более крупными/меньшими и выдает исключение OutOfMemoryException.

Пример: имеется 5 выделенных 96 МБ массивов (200x200x200, 12 байтов данных в каждой записи), и программа должна заменить их 210x210x210 (111MB). Он делает это так же, как это:

array1 = new Vector3[210,210,210];

Где array1-array5 - те же самые поля, которые использовались ранее. Это должно ставить старые массивы в качестве кандидатов на сбор мусора, но, похоже, GC не действует достаточно быстро и оставляет старые массивы выделенными до выделения новых, что приводит к OOM, тогда как если они освобождают до новых распределений, то пространство должно быть достаточно.

То, что я ищу, - это способ сделать что-то вроде этого:

GC.Collect(array1) // this would set the reference to null and free the memory
array1 = new Vector3[210,210,210];

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

Есть ли правильный способ сделать это?

4b9b3361

Ответ 1

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

После просмотра вашего комментария

  • Помещение GC.Collect(); похоже, помогает, он все равно не решает проблему полностью - по какой-то причине программа по-прежнему падает, когда выделено около 1,3 ГБ (я использую System.GC.GetTotalMemory(false), чтобы найти реальную сумму).

Я подозреваю, что у вас может быть фрагментация памяти. Если объект большой (85000 байт в .net 2.0 CLR, если я правильно помню, я не знаю, было ли оно изменено или нет), объект будет выделен в специальной куче, Большая куча объектов (LOH). GC восстанавливает память, используемую недостижимыми объектами в LOH, но не выполняет уплотнение в LOH, как и для других кучек (gen0, gen1 и gen2) из-за производительности.

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

Я могу представить два обходных пути в этот момент.

  • Переместитесь на 64-разрядную машину/ОС и воспользуйтесь ею:) (Самый простой, но, возможно, самый сложный, в зависимости от ваших ограничений ресурсов)
  • Если вы не можете сделать # 1, попробуйте сначала выделить огромный патрон памяти и использовать их (может потребоваться написать некоторый вспомогательный класс для управления меньшим массивом, который на самом деле находится в большем массиве), чтобы избежать фрагментации, Это может немного помочь, но это может не полностью решить проблему, и вам, возможно, придется справиться со сложностью.

Ответ 3

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

array1 = null;
GC.Collect();
array1 = new Vector3[210,210,210];

Ответ 4

Разве это не просто фрагментация кучи большого объекта? Объекты > 85 000 байт выделены на кучу больших объектов. GC освобождает место в этой куче, но никогда не сжимает остальные объекты. Это может привести к недостаточной непрерывной памяти для успешного размещения большого объекта.

Алан.

Ответ 5

Если бы мне пришлось предположить, что проблема не в том, что вы переходите от Vector3 [200,200,200] к Vector3 [210,210,210], но, скорее всего, перед этим вы имеете аналогичные предыдущие шаги:

 i.e.   
    // first you have
    Vector3[10,10,10];
    // then
    Vector3[20,20,20];
    // then maybe
    Vector3[30,30,30];
    //  .. and so on ..
    //  ...
    // then
    Vector3[200,200,200];
    // and eventually you try
    Vector3[210,210,210] // and you get an OutOfMemoryException..

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

Итак, вместо вышесказанного, есть что-то вроде этого:

 // first start with an arbitrary size
 Vector3[64,64,64];
 // then double that
 Vector3[128,128,128];
 // and then.. so in thee steps you go to where otherwise 
 // it would have taken you 20..
 Vector3[256,256,256];

Ответ 6

Они могут не собираться, потому что они упоминаются где-то, чего вы не ожидаете.

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

Ответ 7

Я понимаю, что вы пытаетесь сделать, и настаивать на немедленной сборке мусора, вероятно, не является правильным подходом (поскольку GC тонкий и пугающий).

Тем не менее, если вы хотите эту функциональность, почему бы не создать ее?

public static void Collect(ref object o)
{
    o = null;
    GC.Collect();
}

Ответ 8

Исключение OutOfMemory внутренне запускает цикл GC автоматически один раз и снова пытается выполнить выделение, прежде чем фактически выбросить исключение в ваш код. Единственный способ получить исключения OutOfMemory - если вы держите ссылки на слишком много памяти. Удалите ссылки, как только сможете, присвоив их null.

Ответ 9

Частью проблемы может быть то, что вы выделяете многомерный массив, который представляется как единый непрерывный блок памяти на большой куче объекта (подробнее здесь). Это может блокировать другие распределения, поскольку нет свободного смежного блока для использования, даже если есть где-то какое-то свободное пространство, поэтому OOM.

Попробуйте выделить его как зубчатый массив - Vector3 [210] [210] [210] - который расширяет массивы вокруг памяти, а не как один блок, и видит, улучшает ли это вопросы

Ответ 10

Джон. Создание объектов > 85000 байт сделает объект в большой куче объекта. Большая куча объекта никогда не уплотняется, вместо этого свободное пространство снова используется повторно. Это означает, что если вы каждый раз выделяете большие массивы, вы можете оказаться в ситуациях, когда LOH фрагментирован, следовательно, OOM.

вы можете проверить это в случае разрыва с отладчиком в точке OOM и получения дампа, отправки этого дампа в MS с помощью ошибки подключения (http://connect.microsoft.com) станет отличным началом.

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

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