Speicherfresser Image

  • VB.NET
  • .NET (FX) 4.0

Es gibt 21 Antworten in diesem Thema. Der letzte Beitrag () ist von Olderman.

    Speicherfresser Image

    Hallo,
    erst einmal möchte ich mich für die vielen hilfreichen Tips bedanken, welche ich im Laufe der Zeit hier gesammelt habe.
    Nachdem ich aber seit einigen Tagen verzweifelt recherchiere, möchte ich mein Problem direkt ansprechen.
    Ich möchte mehrere Bilddateien (jpg) öffnen , sortieren, evtl. editieren und kopieren.
    Das Programm läuft, aber wenn ich eine gewisse Zahl von Bildern (ca 10 bis 20) nacheinander geöffnet habe bekomme ich die Fehlermeldung, dass mein
    Arbeitsspeicher nicht ausreicht. (i7, 16GB, W10- 64). Im Taskmanager kann ich beobachten, wie der Speicherbedarf der vshost32.exe ansteigt und bei ca 1,2 GB steigt dann das Programm aus.
    Ich habe mein Problem auf folgende Zeilen heruntergebrochen und vereinfacht.

    Wo liegt mein Denkfehler? Das ist doch nun wirklich kein ungewöhnlicher Code.

    Quellcode

    1. Public Class Form1
    2. Dim m_picture As Image
    3. Private Sub LoadPicture(sender As System.Object, e As System.EventArgs) Handles Label.Click
    4. OpenFileDialog.ShowDialog()
    5. Label.Text = OpenFileDialog.FileName
    6. Dim load_image As Image = Image.FromFile(Label.Text)
    7. 'Sperre der Datei aufheben
    8. Dim copy_image As New Bitmap(load_image)
    9. load_image.Dispose()
    10. m_picture = copy_image
    11. PictureBox.Image = m_picture
    12. End Sub
    13. End Class

    Olderman schrieb:

    load_image.Dispose()

    Jop, aber was ist mit dem Bild das bereits in der PictureBox drin ist? Also vom vorherging Aufruf der LoadPicture Methode? Das muss natürlich auch Disposed werden wenn du ein neues lädst, sonst liegt das alte weiter im Speicher rum.
    Bitte helft einem alten Mann mit Brett vor dem Kopf.
    So?
    Zeile 6. If Not IsNothing(PictureBox.Image) Then PictureBox.Image.Dispose() ?


    Scheint zu funktionieren.
    Herzlichen Dank.
    Ich nehme Euch in mein Gutenachtgebet auf.

    Aber eine Frage habe ich doch noch. Warum verbleibt nur bei der Picturebox.Image das alte Bild im Speicher aber nicht bei der Copy_Image und m_Image ?





    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Olderman“ ()

    Olderman schrieb:

    OpenFileDialog.ShowDialog()
    Was passiert, wenn Du den Dialog mit Cancel beendest?
    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!
    Was passiert, wenn Du den Dialog mit Cancel beendest?

    Danke, ich weiß auf was Du mich hinweisen möchtest, aber das ist wirklich nur die Quintessenz eines Problems mit dem Speicher. Im "richtigem" Programm wird das abgefangen.

    Beide Variablen, und die Image Property zeigen auf die selbe Instanz der Bitmap. Dein m_picture = copy_image kopiert nicht die Bitmap, sondern schreibt quasi in m_picture den selben Zeiger wie in copy_image. Das selbe passiert bei der Zuweisung von PictureBox.Image = m_picture

    Also wäre es egal welche Variable ich 'dipose', das Ergebnis wäre immer das selbe?

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Olderman“ ()

    Olderman schrieb:

    Ich möchte mehrere Bilddateien (jpg) öffnen , sortieren, evtl. editieren und kopieren.
    Für dieses Vorhaben ist der Code, den du uns präsentierst denkbar schlecht.
    Mit dem da oben bindest du im Prinzip das Laden des Bildes IMMER, DIREKT an den Button. Sprich: Wennn du mal den Button löschst oder sich die Anforderung an das Laden des Bildes ändern (nur einen Ausschnitt, ein anderes Bild, ...), dann musst du für jede Änderung den Eventhandler (also Private Sub LoadPicture(sender As System.Object, e As System.EventArgs) Handles Label.Click) umschreiben.

    Besser wäre es eine eigne Funktion zu machen, welcher du den Pfad und alle weiteren Parameter übergibst. Die Funktion liefert dir dann auch ein Bitmap Opjekt zurück.

    PS: Nicht nur an der Stelle ist der Code Mist, auch an anderen Stellen, auf die ich jetzt aber nicht weiter eingehen werde.
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell
    @Olderman
    Du verwendest z.B ein Label als eine Variable, das ist nicht fein (Labels sind zur Anzeige da)...
    Verwende beim Dialog einen Using-Block, dann brauchst du dich um das Aufräumen nicht mehr zu kümmern...
    Und die Image/Bitmap-Verschieberei, kannst du dir auch sparen...
    Ein Beispiel, wie ich das meine:

    VB.NET-Quellcode

    1. Private _picture As Image
    2. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    3. Using dlg = OpenFileDialog1
    4. If dlg.ShowDialog = DialogResult.OK Then
    5. Label1.Text = dlg.FileName
    6. _picture = Image.FromFile(dlg.FileName)
    7. PictureBox1.Image = _picture
    8. End If
    9. End Using
    10. End Sub

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „VB1963“ ()

    Ich glaube ich habe mich nicht richtig ausgedrückt.
    Natürlich ist der Code totaler Mist. Das ist ein Funktion die nur dazu diente Bilder in den Speicher zu schieben um den Speicherbedarf im Taskmanager zu beobachten.
    Das ist kein Programm sondern eine Essenz, die das (nun gelöste) Problem auslöste und in der schnell nach einer Änderung das Ergebnis kontrolliert werden konnte.
    Das Label habe ich nur genommen um den Namen der geladenen Datei zu sehen. Natürlich hätte ich einen Button nehmen können. Ich hätte auch die Picturebox anklicken können.
    Aber im wirklichem "Testprogramm" läuft das über eine Schleife um sich den Einzelaufruf bis zum Absturz zu sparen
    Im richtigem Programm läuft das alles eigentlich über Funktionen und Klassen aber dann wäre das hier kaum darstellbar gewesen. Meine Bezeichnungen sind natürlich sonst auch anders.
    m_Picture ist im Original eigentlich eine Dictionary usw. Ich habe die in Frage kommenden Schritte vereinfacht eingepflegt ohne das diese in diesem Kontext einen Sinn ergeben.
    Ich wollte die Sache nur auf einen Blick erkennbar gestalten.

    Verwende beim Dialog einen Using-Block, dann brauchst du dich um das Aufräumen nicht mehr zu kümmern...
    Und die Image/Bitmap-Verschieberei, kannst du dir auch sparen...

    Mit Using (jedenfalls wie oben im Beispiel) tritt das gleiche Problem auf.
    Ohne das Verschieben in eine Kopie und das 'disposen' der Variablen (_picture) bleibt die Datei für Zugriffe gesperrt. (ist natürlich nur im richtigem Programm wichtig)
    Trotzdem vielen Dank.

    Radinator schrieb:

    PS: Nicht nur an der Stelle ist der Code Mist, auch an anderen Stellen
    Und wie kannst du das beurteilen, wenn der TE doch geschrieben hat: ​Ich habe mein Problem auf folgende Zeilen heruntergebrochen und vereinfacht.? Du weißt ja gar nicht wie sein ganzer Code aussieht. Das ist nur Spekulation anhand eines simplen Code-Beispiels um die Problemstellung zu verdeutlichen.

    @VB1963 Wie soll das denn Funktionieren, wenn der Button mehr als 1 mal gedrückt wird? Das Using kannst du nur benutzen, wenn du auch immer wieder eine neue Instanz von dem OpenFileDialog erzeugst, sonst ist sie nach dem ersten mal Disposed und dann kracht es.

    Olderman schrieb:

    Also wäre es egal welche Variable ich 'dipose', das Ergebnis wäre immer das selbe?

    Ja genau.
    Ok du hast recht, das Funktioniert aber Vorsicht! Das ist ein absoluter Sonderfall, dass du ein Object, nachdem du es Disposed hast weiter verwenden kannst. Das Funktioniert normalerweise so nicht, aber den OpenFileDialog scheint das Dispose einfach nicht zu interessieren. Das ist eigentlich kein valides vorgehen.
    Ich habe den OpenfileDialog im Komponentenfach für die Form bereit gestellt...Using dlg = OpenFileDialog1 ...beachte den 1er
    Ohne die Bereitstellung muss man es so angeben Using dlg As New OpenFileDialog - ist mit Option Strict On gar nicht anders möglich!
    Das ist der Unterschied...(ob da das Using jetzt sinnvoll ist, weis ich jetzt gar nicht? - Auf alle Fälle beinhaltet der Dialog ein IDisposable und man kann das Using verwenden...)

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „VB1963“ ()

    @Bluespide:

    Bluespide schrieb:

    Und wie kannst du das beurteilen, wenn der TE doch geschrieben hat:


    Deswegen:
    1.)

    Olderman schrieb:

    Image.FromFile(Label.Text)

    2.)

    Olderman schrieb:

    Private Sub LoadPicture(sender As System.Object, e As System.EventArgs) Handles Label.Click

    3.)
    Und aus (eigener) Erfahrung sowie Code Snippets in diesem Forum kann ich sagen, dass jemand, der sowas (also Code und GUI mischen, Laden von Resourcen in Eventhandlern) in einem Teil/Ausschnitt seines Codes hat, dessen Code wird im Rest auch nicht besser sein :(

    Lg Radinator
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell

    Deswegen:
    1.) Olderman schrieb: Image.FromFile(Label.Text)

    2.)Olderman schrieb: Private Sub LoadPicture(sender As System.Object, e As System.EventArgs) Handles Label.Click

    3.)
    Und aus (eigener) Erfahrung sowie Code Snippets in diesem Forum kann ich sagen, dass jemand, der sowas (also Code und GUI mischen, Laden von Resourcen in Eventhandlern) in einem Teil/Ausschnitt seines Codes hat, dessen Code wird im Rest auch nicht besser sein


    Es gibt keine Rest vom Code, da das Programm nur diese eine Aufgabe (Speicherbedarf) hatte. Und dafür war es möglichst kurz und basierte auf immer weiter reduzierte Grundlagen, weshalb es immer blöder wurde. Ich hätte das Label auch noch weglassen können und den Dialog mit sonst etwas starten können, aber es war da und gab mir die Info der gerade geladenen Datei. Natürlich ist der Code Mist, aber so etwas wirst Du in den über 2000 Zeilen des richtigen Codes nicht finden.
    Aber ich bin jetzt gewarnt. Das Problem deutlich zu machen reicht nicht (Das es mir gelungen ist sieht man daran, dass die beiden ersten Antworten es gelöst haben.) Auch der sonstige Code wird seziert, analysiert und daraus Rückschlüsse auf den Geisteszustand des Programmierers gezogen.
    Ich komme mir ein bisschen vor wie ein Tennisspieler der bei einer Grillparty nach einem Tip für einen Aufschlag fragt und als Antwort bekommt, dass man mit fettigen Fingern kein Tennis spielt. Irgendwie ja richtig, aber doch am Thema vorbei.
    Ich weiß aber, dass es ja nur gut gemeint ist und deswegen noch einmal vielen Dank.

    Hast du das Schnipsel schon einmal probiert?

    Ich hab es probiert und es läuft - aber nur bis der Speicher nicht mehr reicht und das war ja eigentlich mein Problem.

    @Olderman Also vor dem Aufschlag die Hände waschen. ;)
    Du schreibst viele Sätze in Deine Posts, die als Zitat wirken, ohne den Scheiber selbst zu zitieren. Das ist suboptimal, da so kein Schreiberling merkt, dass er zitiert worden ist.
    Der Forum-Editor bietet Dir die Möglichkeit, diese Zitate als "echte" Zitate zu layouten, indem Du nach dem Markieren auf die Blase "Auswahl zitieren" klickst.
    Wenn Du danach Deinen Post startest, sind die Zitate drin.
    Hast Du ihn bereits gestartet, kommt unten rechts eine weitere Blase mit "Zitate (ANZAHL)". Da klickst Du dann drauf und kannst die Zitate in Deinen Post reinholen.
    Aber
    Bitte nicht die ganzen Posts über Dir zitieren, das stört.
    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!