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

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

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

Метод тега выглядит следующим образом:

private static Image TagAsProductImage(Image image)
{
    try
    {
        // Prepares the garbage collector for added memory pressure (500000 bytes is roughly 485 kilobytes).
        // Should solve some OutOfMemoryExceptions.
        GC.AddMemoryPressure(500000);

        using (Graphics graphics = Graphics.FromImage(image))
        {
            // Create font.
            Font drawFont = new Font("Tahoma", image.Width*IMAGE_TAG_SIZE_FACTOR);

            // Create brush.
            SolidBrush drawBrush = new SolidBrush(Color.Black);

            // Create rectangle for drawing.
            RectangleF drawRect = new RectangleF(0, image.Height - drawFont.GetHeight(), image.Width,
                                                    drawFont.GetHeight());

            // Set format of string to be right-aligned.
            StringFormat drawFormat = new StringFormat();
            drawFormat.Alignment = StringAlignment.Far;

            // Draw string to screen.
            graphics.DrawString(TAG_TEXT, drawFont, drawBrush, drawRect, drawFormat);
        }
    }
    // If an out of memory exception is thrown, return the unaltered image.
    catch(OutOfMemoryException)
    {
        GC.RemoveMemoryPressure(500000);
        return image;
    }

    GC.RemoveMemoryPressure(500000);
    return image;
}

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

У нас были проблемы с OutOfMemoryExceptions при достижении using (Graphics... (когда изображение должно быть извлечено с сервера до отметки, если изображение существует в кеше, тегирование не было проблемой).

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

Сначала я попытался сделать общий GC.Collect(); перед вызовом Graphics.FromImage(image), который работал (конечно), но мне не нравится принудительно собирать, так как он сильно ударил по производительности.

Мой второй подход заключался в вызове GC.Collect() в catch-statement, а затем рекурсивном вызове TagAsProductImage(image), но это может привести к бесконечному циклу, если GC не освободит достаточное количество памяти.

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

Возможно, мне удастся использовать GC.Collect(), поскольку вся работа по получению изображения из тега обслуживания → сохранение → довольно велика, поэтому производительность, получаемая от сбора, будет минимальной, но я бы действительно как лучшее решение.

Если у кого-то есть разумное решение, делитесь им.

4b9b3361

Ответ 1

Если вы ищете способ убедиться, что у вас достаточно памяти для операции, используйте MemoryFailPoint.

При этом через using вы можете определить область, в которой вам понадобится определенный объем памяти. Если он недоступен, он выкинет восстанавливаемый InsufficientMemoryException.

Подробнее см. http://msdn.microsoft.com/en-us/library/system.runtime.memoryfailpoint.aspx.

Ответ 2

У вас другая проблема, этот код использует очень мало памяти. К сожалению, исключения GDI + довольно сложны. Диагностируйте это с помощью TaskMgr.exe, вкладки Процессы. View + Select Columns и отметьте объекты GDI, дескрипторы и объекты USER.

Если мое подозрение верное, вы увидите счетчик объектов GDI, чтобы ваш процесс постоянно поднимался по мере запуска этого кода. Когда он достигает 10 000 Windows, решает, что в коде есть что-то принципиально неправильное и отказывается создавать какие-либо ручки. GDI + затем немного понемногу об этом и сообщает об ошибке в памяти. Неправильно, это должно было быть ошибкой "не удалось создать дескриптор". Код ошибки, которого у него нет..NET неспособен улучшить исключение.

Anyhoo, причина в том, что вы не вызываете Dispose() на шрифт и кисть. Оберните их с помощью инструкции using. Обычно это не вызывает проблем, но ваша программа, по-видимому, использует слишком мало собранной памяти, чтобы когда-либо запускать поток финализатора.