Принудительная сборка мусора массивов, C # - PullRequest
17 голосов
/ 09 июля 2009

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

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

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

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

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

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

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

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

Ответы [ 10 ]

15 голосов
/ 09 июля 2009

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

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

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

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

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

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

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

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

Куча больших объектов

CLR Обнаружена куча больших объектов наизнанку

Вы можете проверить, нет ли у вас проблем с фрагментацией, используя SOS

Проверьте этот вопрос , чтобы узнать, как использовать SOS для проверки loh.

7 голосов
/ 09 июля 2009

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

array1 = null;
GC.Collect();
array1 = new Vector3[210,210,210];
4 голосов
/ 09 июля 2009

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

Алан.

2 голосов
/ 09 июля 2009

Если бы мне пришлось предположить, что ваша проблема на самом деле не в том, что вы переходите из 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];
1 голос
/ 09 июля 2009

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

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

public static void Collect(ref object o)
{
    o = null;
    GC.Collect();
}
1 голос
/ 09 июля 2009

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

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

0 голосов
/ 10 июля 2009

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

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

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

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

0 голосов
/ 09 июля 2009

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

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

0 голосов
/ 09 июля 2009

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

...