2009-05-19 8 views
4

Ich leide an einer OutOfMemoryException beim Abrufen eines Image aus einer ImageList Ich konnte keine geeignete Lösung für das Problem finden.ImageList/Image OutOfMemoryException

Ich habe ein benutzerdefiniertes ListView-Steuerelement, das ein Ereignis für das Zeichnen von ListViewItems angefügt hat. Dies ruft dann eine statische Methode auf, die den Gegenstand zeichnen soll.

Für eine ListView von etwa 300 Elementen, bekommen wir den Speichersprung um 100 MB jedes Mal, wenn die ListView gescrollt wird. Der säumige Code wurde auf die folgende aufgespürt:

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); 
} 

Es scheint (sicherlich auf WinXP), dass die Garbage Collection nicht richtig funktioniert, so dass der Speicher winden. Wir haben versucht, image.Dispose() direkt nach dem Codeblock hinzuzufügen, um das Problem zu beheben. Dies hat jedoch keine Auswirkungen.

Die einzige Lösung, die ich bisher gefunden habe, ist am Ende der statischen Methode, GC.Collect() aufzurufen. Das Problem dabei ist jedoch, dass es dann dazu führt, dass sich das ListView langsam neu malt und Sie Artefakte auf dem Bildschirm erhalten, während es versucht, es erneut zu zeichnen.

Hat jemand anderes das erlebt? Oder kennt einen Workaround?

+0

Ich habe OutOfMemoryExceptions auf zwei verschiedene Anwendungen erfahren, keiner von ihnen Bilder oder Listview beteiligt sind. Unglücklicherweise war die einzige Lösung, die ich finden konnte, GC_Collect() immer wieder aufzurufen. – Crispy

Antwort

4

Verfügen Sie über graphics? Auch wenn Sie Ihr Bild wie erwähnt verwerfen, müssen Sie sicherstellen, dass es aus der ImageList genommen wird, oder Sie werden mehr Probleme verursachen. Welches Format haben die Bilder?

Im Allgemeinen, wenn Sie Probleme mit dem Speicher haben, wenn Bilder beteiligt sind, ist Ihr Problem entweder eine Methode nicht wie ein Bildformat, oder 9/10 Mal, Sie missverstanden den Lebenszyklus eines der Grafikobjekte.

  • Überprüfen Sie alle Ihre Graphics Nutzung und sie in using Blöcke setzen.
  • Ihr Image Lebenszyklus prüfen und mit zu kopieren, entsorgen sie, schließen zugrunde liegenden Ströme usw.
  • Legen Sie bis einen Speichermanager vorsichtig sein (VS2008 hat eine eingebaut) und sehen, was schön ist nicht immer aufgeräumt.

EDIT:

Hier ist die beste Option, die ich finden kann, ImageList.Draw (graphics, x, y, width, height, index) verwenden. Dies verwendet den internen Griff, anstatt eine Kopie des Bildes zu erstellen.

+0

Danke für die Antwort. Das ist ein guter Punkt über das Image, ich werde wahrscheinlich diese Zeile entfernen und die Garbage Collection einfach verlassen müssen. Ich möchte das Grafikobjekt nicht entsorgen, da ich es von den EventArgs nehme, und daher weiß ich nicht, ob es später an anderer Stelle verwendet wird. Die Bilder variieren, die Zeit, die wir gesehen haben, war eine Icon-Datei, daher verwenden wir ein Icon.ToBitmap(). Wird versuchen, den Speichermanager aufzuspüren, das klingt sehr nützlich. – Ian

+0

Ich hatte nur einen kurzen Versuch, das Speicherwerkzeug zu verwenden. Es scheint jedoch, dass jedes Mal, wenn ich item.ImageList.Images [item.ImageKey] rufe, ein neues Bild herauskommt, anstatt einen Verweis auf einen vorhandenen. Zum Beispiel kann ich item.ImageList.Images [item.ImageKey] .Dispose() und dann rufen Sie item.ImageList.Images [item.ImageKey] und immer noch einen Wert aus. Sieht also eher seltsam aus. Bis jetzt hat es mir nicht geholfen, das Problem zu lösen. – Ian

+0

Nach dem Lesen der ImageCollection.Item-Eigenschaften in MSDN habe ich festgestellt, dass eine Kopie zurückgegeben wird. Der Aufruf von image.Dispose() ist in diesem Fall der richtige Ansatz. Ich rate aber, weil es zu viele Bilder gibt, die Zeichnung Thread + Ereignisse verbrauchen diesen Speicher, während der Garbage Collector darauf wartet, dass diese Prozesse vor dem Auslösen zu beenden. Wenn wir zu viele Bilder haben, fällt es um, bevor dieser Prozess stattfinden kann ... – Ian

0

Ich habe es geschafft, dieses Problem in meiner Anwendung zu lösen.

Jason hat die Antwort, Sie müssen sicherstellen, dass Sie "using" Blöcke oder ihre Entsprechung verwenden.

Ich benutze VB, und das Äquivalent war zu verwenden Try ... Catch ... Schließlich, wann immer ich ein neues Bitmap erstellte, BitMap.Dispose aufrufen und die Bitmap = nichts im "Finally" -Teil setzen.

Dies scheint ein wirklich häufiges Problem zu sein, aus den Stunden zu schließen, die ich damit verbracht habe, Google zu versuchen. Der folgende Code ermöglicht auch, dass jedes Bild sein Seitenverhältnis beibehält, wenn es auf ein Thumbnail reduziert wird - ein weiteres Problem, das Google schwer fällt!

Code:

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 
Verwandte Themen