Сегментация большой кучи, работа с большими массивами данных
Подскажите пожалуйста, что делать в возникшей ситуации:
Нужно считать TIFF-файл объемом 600мб шириной 13000 пикселей, высотой 20000 пикселей. Привести палитру из 24бит к 16 цветам.
Использую BitmapSource и TIFFBitmapDecoder. Считываю N строк в массив байт, равные BitPerPixel / 8 * Width * N методом CopyPixels. Обрабатываю по 3 байта, превращая в цвет, обрабатываю цвет, записываю в новый массив размером width * height индекс цвета в палитре. В конце создаю BitmapSource.Create новый BitmapSource на основе полученных данных, записываю в файл с помощью TIFFBitmapEncoder.
Что в итоге: на каждой итерации цикла чтения, у меня формируется массив размером 13000 * 3 * (100~500) => 3900000 ~ 19500000 байт. Из-за своего размера он попадает в Большую Кучу, предназначенную для долговременного хранения файлов. При попытке вызвать BitmapSource.Create, я получаю OutOfMemoryException (софтина к тому времени отжирает порядка 1Гб оперативки). Сегментированная куча и висящие в ней массивы чудовищных размеров.
Что примечательно: вызов GC.Collect() никак (!!!) не влияет на память - она просто не освобождается. Более того, даже после выхода из метода, то есть когда жизненный цикл переменной подходит к концу, память продолжает оставаться занятой и вторую картинку уже не обработать.
Я понимаю, что читать\писать такие объемы - глупо, однако указанные классы\методы работают именно с полными наборами байт. Метод же CopyPixels работает, похоже, напрямую с файлом, так как чтение по 1ой строке и по 500 строк, во втором случае быстрее раз в... 500.
В виду этого имеется ряд вопросов:
1) Как освобождать большую кучу?
2) Почему она не освобождается сама до завершения программы?
3) Как избежать заполнения и сегментации большой кучи (сейчас подумал, что стоит вынести массив-буфер за пределы цикла, хоть hardcase, в свое время, и говорил, что переменные надо объявлять в той области видимости, где они используются)
И два вопроса менее абстрагированных, которые не заслуживают отдельных тем:
1) Есть ли особенности использования BitmapSource? У него нет метода Dispose, но освобождаться самостоятельно он не спешит.
2) Есть ли другие, менее затратные способы чтения\записи изображений (tiff, gif, pcx, bmp, png), позволяющие работать с небольшими буфферами (меньше 85000 байт) и контролировать работу с памятью? (свободные библиотеки предлагать, самостоятельное описание всех форматов - нет).
Заранее благодарю за любую помощь!
- Вызывался GC.Collect(2)? от hardcase, 22 марта 2012 года
Marshal.AllocHGlobal. Работатьс ней (читать-писать) можно используя unsafe расширения C#. Альтернативой этому подходу является использование неуправляемых возможностей C++/CLI.
Выделяем память с помощью
Я уже как то писал на форуме по этому поводу - и как раз таки по вопросу обработки картинок - проблема в том, что данная память не помеченная как мусор - поэтому сборщик памяти и не чистит ее. Причина - объекты не достигли своей конца области видимости например, на них существуют ссылки и т.п.
Решением - действительно вынести выделение памяти за пределы цикла, но не видя кода трудно рекомендовать решение.
Учитывая неординарность данной проблемы надо поискать компоненты, которые возможно решают проблему - но вполне возможно что прийдется писать их самому, и возможно используя неуправляемый код. Потому как все же tiff объемом в 600 метров - ну как бы не слишком ординарная задача. ИМХО
Один из вариантов - выносить большие объемы данных в неуправляемую кучу, так работает Paint.NET
"Принудительно запускает немедленную сборку мусора для всех поколений."
Это неправда?
А можно поподробнее про неуправляемую кучу?
Для работы с большими файлами еще можно использовать