Вопрос

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

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

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];

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

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

Это было полезно?

Решение

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

Увидев ваш комментарий,

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

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

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

На данный момент я могу придумать два обходных пути.

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

Другие советы

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

Большая куча объектов

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

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

Проверь это вопрос для примера того, как использовать SOS для проверки loh.

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

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

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

Алан.

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

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

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

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

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

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top