Предотвращение OutOfMemoryException с помощью GC.Collect ()

1

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

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

bool Multithread = CheckMultithread();
UpdateParameters();

if (Multithread)
{
    Parallel.For(0, FileNames.Length ,i => Solve(FileNames[i]));
}
else
{
     foreach (string s in FileNames)
     {
         Solve(s);
     }
}

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

Проблема в том, что если многопоточность включена, я получаю исключение OutOfMemoryException, когда я достигаю 200-го или около того изображения. Очевидно, что такой параллелизм (где практически весь код работает в разных потоках) потребляет в N раз больше памяти (с N = = количество запущенных потоков)...

Я пересмотрел каждый класс, который я создал, и только один использует неуправляемый код (QuickBitmap.cs, оболочка для класса Bitmap, используя блокировки и разблокировки для повышения производительности). Но я реализовал интерфейс IDisposable в этом классе, поэтому... Я не знаю, почему это происходит.

Для каждого потока требуется + / - 400 мб бара для работы. Когда вызывается OutOfMemoryException, программа использует около 1300 мб оперативной памяти. Даже у меня более 9 ГБ свободной памяти

Чтобы справиться с этим исключением, я добавляю следующий код прямо в просьбе Solve()

        if (GC.GetTotalMemory(false) > 1000*1000*1000)
        {
            lock (Manager.dasLock)
            {
                Manager.sw.Start();
                GC.Collect();
                Manager.sw.Stop();
            }
        }

ProcessedCount - это статический int, который увеличивается внутри оператора lock().

После добавления этой части кода программа может обрабатывать все изображения (2000+) без каких-либо исключений. Я также никогда не использую более 1,5 ГБ оперативной памяти.

Но так как все ПЛАМЯЮТ меня, чтобы DARING позвонил всемогущему GC... Что я могу сделать, чтобы это предотвратить?

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

Ps: в последних строках Solve() я вызываю Dispose каждого объекта с неуправляемыми ресурсами и устанавливаю для всех моих vars значение null.

Pps: да, это 32-битное программное обеспечение, но ограничение должно составлять 4 ГБ вместо 1,5, верно?

Edit3: измените код вызова GC.Collect. Просто я могу понять, сколько времени я трачу на сбор. За каждую минуту обработки я трачу 0,4 сбора.

  • 0
    Вы пытались сохранить обработанные изображения и утилизировать их вручную после сохранения? И ограничение оперативной памяти 1,3 ГБ из-за 32-битной;)
  • 0
    Ваше предположение, что Parallel.For создает поток для каждой итерации, неверно. Он разбивает коллекцию на несколько групп, и каждая группа запускается в потоке.
Показать ещё 6 комментариев
Теги:
garbage-collection
out-of-memory
task-parallel-library

2 ответа

1
Лучший ответ

Ограничение количества потоков решило проблему:

        if (Multithread)
        {
            ParallelOptions pOptions = new ParallelOptions();
            pOptions.MaxDegreeOfParallelism = Environment.ProcessorCount;
            Parallel.For(0, FileNames.Length, pOptions, i => Solve(FileNames[i]));
        }
        else
        {
            foreach (string s in FileNames)
            {
                Solve(s);
            }
        }
0

Хотя не указано, если вы занимаетесь обработкой изображений, есть хорошая вероятность, что некоторые из объектов, с которыми вы имеете дело, реализуете интерфейс IDisposable - например, объекты File или Image.

Для всех этих объектов вы должны попытаться использовать "использование" блоков (см. Здесь).

Скорее всего, когда вы вызываете GC.Collect, он избавляется от этих объектов, поэтому из того, что я понимаю, когда вы вызываете GC.Collect, вы не получаете исключений

  • 0
    Я знаю. Но, как я уже сказал, я вызываю Dispose для каждого объекта, подобного этому.
  • 0
    Вы говорите, что называете это в конце Решения. Вы пытались установить точку останова, чтобы подтвердить, что вы достигли последней строки?

Ещё вопросы

Сообщество Overcoder
Наверх
Меню