Image-Array wie behandeln?

  • VB.NET
  • .NET (FX) 4.0

Es gibt 42 Antworten in diesem Thema. Der letzte Beitrag () ist von ~blaze~.

    Image-Array wie behandeln?

    Ich verwende unter VB10 folgendes Array:

    Dim IMG() As Image

    und mache vor der Verwendung ein

    ReDim IMG(ANZ - 1)

    Das Array wird mit Bitmaps von Thumbnails der Bilder eines Verzeichnisses belegt. Wenn das Verzeichnis wechselt, wird wieder der Befehl Redim ... gegeben und es kommen andere Images in die Elemente. Alternativ dazu wäre es möglich, den Positionen des unveränderten Arrays einfach neue Images zuzuweisen.

    Was geschieht in beiden Fällen mit dem Speicherplatz der Images? Wird das Dispose für die nicht mehr benötigten Images mit dem Redim und/oder dem Überschreiben automatisch durchgeführt oder was wäre für einen sauberen Ablauf zu tun. Welche der beiden Methoden gewinnt hinsichtlich Rechenzeit? Gibt es evtl. deutliche Unterschiede? Was geschieht, wenn ich einem Element Nothing zuweise? Wird dann der Speicherplatz automatisch freigegeben.

    Bevor ich konkrete Experimente mache, hätte ich gern erst mal Meinungen gehört.

    drschef schrieb:

    Visual Basic-Quellcode

    1. ReDim IMG(ANZ - 1)
    Schmeiß den VB6-Ranz raus und nimm eine List(Of Image).
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    drschef schrieb:

    Wird das Dispose für die nicht mehr benötigten Images mit dem Redim und/oder dem Überschreiben automatisch durchgeführt
    Nein.
    Der Dispose-Pattern ist genau dafür da, dass der Programmierer die Bereinigungen explizit aufruft, die nicht vollkommen automatisierbar (GarbageCollection) sind.

    Das mit dem Array-Redim ist zwar ungewöhnlich, hat aber in diesem Fall keinen Nachteil gegenüber einer List(Of Image), die man stattdessen vor Neu-Befüllung halt löschen würde.
    Wichtig ist halt, dass die Anzahl von vornherein bekannt ist, und festgelegt wird, damit nicht ständig umdimensioniert werden muss.
    Schmeiß mal Image durch'n Decompiler. Da steht:

    C#-Quellcode

    1. ~Image()
    2. {
    3. this.Dispose(false);
    4. }

    bzw.

    C#-Quellcode

    1. protected virtual void Dispose(bool disposing)
    2. {
    3. if (this.nativeImage != IntPtr.Zero)
    4. {
    5. try
    6. {
    7. SafeNativeMethods.Gdip.GdipDisposeImage(new HandleRef(this, this.nativeImage));
    8. }
    9. catch (Exception ex)
    10. {
    11. if (ClientUtils.IsSecurityOrCriticalException(ex))
    12. {
    13. throw;
    14. }
    15. }
    16. finally
    17. {
    18. this.nativeImage = IntPtr.Zero;
    19. }
    20. }
    21. }

    Habs auch selbst ausprobiert, das Bild wird 100%tig freigegeben. So können keine Memoryleaks entstehen. Das Problem an der Sache ist, du weißt nicht wann der GC es freigibt (spätestens nach Programmende halt). Da man im Normalfall Sachen wie Dateien etc schnellstmöglich freigeben will, ruft man Dispose() manuell auf.
    Dann probierma das:

    VB.NET-Quellcode

    1. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    2. Dim bla = Image.FromFile("C:\Programming\Ressources\TestPictures\du bist faul.jpg")
    3. End Sub
    und - während das Prog noch läuft - die BildDatei im Explorer umbenennen.

    Gonger96 schrieb:

    Das Problem an der Sache ist, du weißt nicht wann der GC es freigibt

    Zu dem Zeitpunkt gibt er es anscheinend noch nicht frei ;) Aber wie gesagt spätestens nach Programmende wird es freigegeben, da der GC den Finalzer aufruft. Kannst ja selber in die Source gucken
    Sie wird trotzdem freigegeben. Wann das geschieht und ob's praktikabel ist, ist ne andere Frage. Es ist aber Fakt. Ich muss Dispose nicht aufrufen, allerdings kann ich es, falls ich Resourcen zu einem bestimmten Zeitpunkt freigeben will. Wenn die Instanz des Interfaces nicht mehr gebraucht wird, dann sollte man das auch tun. Nötig ists aber nicht.
    Hi
    es wird freigegeben, sofern es keine (nicht-schwache) Referenz mehr auf das Objekt gibt. Die GC ruft bei Finalisierung des Objekts eben den Finalizer auf. Wie Gonger96 bereits erklärt hat, wird es genau dann freigegeben, wann die GC es für angemessen hält. Dispose wird oftmals dann eben im Finalizer ebenfalls aufgerufen, um das Objekt korrekt freizugeben.

    Das korrekte Dispose-Pattern sieht nicht umsonst so aus, dass man an die Methode Dispose noch übergibt, ob tatsächlich ein Dispose-Aufruf oder ein Finalize-Aufruf durchgeführt wird (dafür steht das disposing bei Protected Overridable Sub Dispose(disposing As Boolean) bzw. protected virtual void Dispose(bool disposing))

    C#-Quellcode

    1. System.Drawing.Image x = System.Drawing.Image.FromFile(@"F:\Users\\Pictures\blaze.jpg");
    2. WeakReference reference = new WeakReference(x);
    3. x = null;
    4. GC.Collect();
    5. if (!reference.IsAlive)
    6. Console.WriteLine("Object has been finalized.");
    7. else
    8. Console.WriteLine("Object has not been finalized.");

    Obiger Code gibt bei mir "Object has been finalized." zurück. Ohne den Aufruf von GC.Collect hat sich nicht viel getan (hatte über Threading.Thread.Sleep gewartet).

    Viele Grüße
    ~blaze~

    ~blaze~ schrieb:

    es wird freigegeben, sofern es keine (nicht-schwache) Referenz mehr auf das Objekt gibt. Die GC ruft bei Finalisierung des Objekts eben den Finalizer auf. Wie Gonger96 bereits erklärt hat, wird es genau dann freigegeben, wann die GC es für angemessen hält.
    Jahaa, so die Theorieieie!
    Inne Praxis, beim Image, bedeutet das nun aber, dass das Image erst beim Beenden des Programms freigegeben wird, man könnte auch sagen: gar nicht. :P
    Es ist an der Zeit, dass ich mich als Auslöser der Diskussion wieder einschalte. Mit so einem Ansturm hatte ich nicht gerechnet. Erstmal vielen Dank für die engagierten Reaktionen. Für mich ist das Resume, dass man mit dem Dispose auf der sicheren Seite ist. Und dann ist es wohl auch besser, das vorhandene Array mit bleibender Dimensionierung neu mit Inhalt zu füllen, als es vorher noch mit einem Redim (ohne Übernahme des Inhalts) neu zu intialisieren, denn das dürfte auch Zeit kosten. Die erhoffte Garantie, dass das Redim ohne Übernahme von Inhalten gleich reinen Tisch macht, wäre eigentlich logisch, damit sich der Speicher nicht heiß läuft, aber da wir's nicht wissen...

    Ich werde es mal so angehen. Bei dem List(of..) ist mir noch nicht ganz klar, wo der Vorteil liegt.
    Das Bild wird relativ sicher auch irgendwann aufgerufen, wenn das Programm entsprechenden Speicherverbrauch hat.
    @EDR: es ist nicht nur Theorie, es ist auch in der Praxis so, wir sind hier bei einer gemanageden Sprache, der Grund warum man das zeug immer frei gibt ist nur, weil man weiß, dass man es nicht mehr braucht, aber MemoryLeaks gibt es eben nicht.

    @TE: List(Of) hat sogar einen sehr großen Vorteil gegenüber Redim.
    Was Redim macht im Prinzip dasselbe wie das neu erstellen eines Arrays

    Visual Basic-Quellcode

    1. Dim bla() As Image
    2. 'Redim
    3. bla = New Image(Anz-1)
    4. 'Redim Preserve
    5. Dim tmp(Anz-1) As Image
    6. For i As Integer = 0 To bla.Length -1
    7. tmp(i) = bla(i)
    8. Next
    9. bla = tmp

    List(Of ) macht genau das was Redim preserve macht, aber sie macht es nur dann, wenn das Array zu klein wird und dann wird nicht nur ein Element hinzugefügt, es wird direkt verdoppelt. Alles auf Kosten von RAM(hat man idr. genug), jedoch wird wesentlich weniger Performanz verwendet. Außerdem wird bei einem Clear nicht gleich der ganze Speicher aufgegeben, sondern es wird Speicher wiederverwendet. Genau dasselbe könnte man mit Redim/Redim Preserve erreichen mit selber Performanz. Aber 1. ist Redim noch ein Relikt aus VB 6 Zeiten, welche ich nicht unbedingt verwenden würde, ich denke die sind nicht unbedingt so gut lesbar. 2. Warum nochmal machen, wenn es das bereits gibt - mit noch wesentlich mehr Funktionalität.
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---