Список изображений / Исключение Image OutOfMemoryException

StackOverflow https://stackoverflow.com/questions/882619

Вопрос

Я страдаю от исключения OutOfMemoryException при получении изображения из ImageList Я не смог найти подходящее решение проблемы.

У меня есть пользовательский элемент управления ListView, к которому прикреплено событие для отрисовки элементов ListViewItems.Затем это вызывает статический метод, который предназначен для рисования элемента.

Для ListView, содержащего около 300 элементов, мы получаем скачок объема памяти примерно на 100 МБ при каждом прокручивании ListView.Код - нарушитель был отслежен до следующего:

Image image = item.ImageList.Images[item.ImageKey];
if (image != null)
{
    Size imageOffset = new Size((bounds.Width - image.Width) / 2, 2); 
    Point imagePosition = bounds.Location + imageOffset;
    graphics.DrawImageUnscaled(image, imagePosition);
}

Кажется (конечно, в WinXP), что сборка мусора работает некорректно, что приводит к спирали памяти.Мы попытались добавить изображение.Dispose() непосредственно после блока кода, чтобы устранить проблему, но это не имеет никакого эффекта.

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

Кто-нибудь еще испытывал это?Или знает об обходном пути?

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

Решение

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

В общем, когда у вас возникают проблемы с нехваткой памяти при использовании изображений, ваша проблема будет заключаться либо в том, что какому-то методу не нравится определенный формат изображения, либо в 9/10 раз вы неправильно поняли жизненный цикл одного из графических объектов.

  • Проверьте все ваши Graphics использование и поместите их в using блоки.
  • Проверьте свой Image жизненный цикл и будьте осторожны с их копированием, удалением, закрытием базовых потоков и т.д.
  • Загрузите диспетчер памяти (в VS2008 есть встроенный) и посмотрите, что не очищается должным образом.

Редактировать:

Вот лучший вариант, который я могу найти, используйте Список изображений.Нарисуйте(graphics, x, y, width, height, index).При этом будет использоваться внутренний дескриптор вместо создания копии изображения.

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

Мне удалось решить эту проблему в моем приложении.

У Джейсона есть ответ: вы должны убедиться, что используете блоки "using" или их эквивалент.

Я использую VB, и эквивалентом было использование Try ...Поймать...Наконец, всякий раз, когда я создавал новое растровое изображение, вызывая BitMap.Dispose и устанавливая Bitmap = nothing в части "Finally".

Похоже, это действительно распространенная проблема, судя по тем часам, которые я потратил, пытаясь найти это в Google.Приведенный ниже код также позволяет любому изображению сохранять свои пропорции при уменьшении до миниатюры, что является еще одной проблемой, которую, похоже, трудно найти в Google!

Код:

Private Function AspectedImage(ByVal ImagePath As String, ByVal SizeWanted As Integer) As Image

    Dim myBitmap, WhiteSpace As System.Drawing.Bitmap
    Dim myGraphics As Graphics
    Dim myDestination As Rectangle
    Dim MaxDimension As Integer
    Dim ReductionRatio As Double

    Try

        'create an instance of bitmap based on a file
        myBitmap = New System.Drawing.Bitmap(ImagePath)



        'create a new square blank bitmap the right size
        If myBitmap.Height >= myBitmap.Width Then MaxDimension = myBitmap.Height Else MaxDimension = myBitmap.Width
        ReductionRatio = SizeWanted / MaxDimension
        WhiteSpace = New System.Drawing.Bitmap(SizeWanted, SizeWanted)

        'get the drawing surface of the new blank bitmap
        myGraphics = Graphics.FromImage(WhiteSpace)

        'find out if the photo is landscape or portrait
        Dim WhiteGap As Double

        If myBitmap.Height > myBitmap.Width Then 'portrait
            WhiteGap = ((myBitmap.Width - myBitmap.Height) / 2) * -1
            myDestination = New Rectangle(x:=CInt(WhiteGap * ReductionRatio), y:=0, Width:=Int(myBitmap.Width * ReductionRatio), Height:=Int(myBitmap.Height * ReductionRatio))
        Else 'landscape
            WhiteGap = ((myBitmap.Width - myBitmap.Height) / 2)
            'create a destination rectangle
            myDestination = New Rectangle(x:=0, y:=CInt(WhiteGap * ReductionRatio), Width:=Int(myBitmap.Width * ReductionRatio), Height:=Int(myBitmap.Height * ReductionRatio))
        End If

        'draw the image on the white square
        myGraphics.DrawImage(image:=myBitmap, rect:=myDestination)
        AspectedImage = WhiteSpace

    Catch ex As Exception
        myBitmap = New System.Drawing.Bitmap("")
        AspectedImage = New System.Drawing.Bitmap(4, 4)
        ImageBufferExceeded = True
        MsgBox("Image Buffer exceeded, too many images in memory. If the one(s) you want can't be seen, please restart the application and navigate straight to your images")
    Finally
        myBitmap.Dispose()
        myBitmap = Nothing
        WhiteSpace = Nothing
    End Try

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