Bildgrösse auslesen verursacht OutOfMemory

  • VB.NET

Es gibt 11 Antworten in diesem Thema. Der letzte Beitrag () ist von Niko Ortner.

    Bildgrösse auslesen verursacht OutOfMemory

    Hallo,
    das von mir unter [Beta] FastGraphicsLib 1.0.0.5
    beschriebene Programm hat eine Schleife, in der aus einer Liste von Bildern die Grösse (.size) überprüft wird. Bilder mit richtiger Grösse werden dann in eine weitere Liste übernommen. Leider finde ich nicht die richtige Methode zum auslesen. Ab 50 Bildern (8 megapixel) ist Schluss, mache ich ein build für 64 bit gehts noch etwas weiter...

    im Bild: etwas über 3 GB ram...und Schluss. Zu dumm, das das Programm am Ende auf x86 laufen soll ;(

    Die schuldige Schleife ist die hier:

    VB.NET-Quellcode

    1. For i As Integer = 1 To Dateiliste.Length - 1
    2. If Image.FromFile(Dateiliste(i)).Size = bsize Then
    3. 'If Image.FromFile(Dateiliste(i)).Size = bsize Then
    4. ReDim Preserve Liste(ind)
    5. Liste(ind) = Dateiliste(i)
    6. ind += 1
    7. End If
    8. Next

    ..und wenn die losläuft um 50 Bilder zu prüfen füllt sich der Ram kontinuierlich:

    Sicher kann man die Grösse auch aus dem jpg Header auslesen, aber das überfordert mich als Anfänger komplett (nach 4h aufgegeben). Ausserdem müsste ich das dann für alle Fromate machen - was für ein Aufwand. Ich verstehe einfach nicht warum der ram nach dem auslesen der Bildgrösse mittels Image.FromFile(Dateiliste(i)).Size zuwuchert. Mir schwant das ich irgendwas wichtiges weggelassen hab (.dispose hat nix genutzt). Für Hinweise oder Vorschläge wie das besser geht bin ich dankbar.
    Gruss, emil
    Mach das mit Using!

    VB.NET-Quellcode

    1. For i As Integer = 1 To Dateiliste.Length - 1
    2. Using img As Image = Image.FromFile(Dateiliste(i))
    3. If img.Size = bsize Then
    4. ReDim Preserve Liste(ind)
    5. Liste(ind) = Dateiliste(i)
    6. ind += 1
    7. End If
    8. End Using
    9. Next
    Mit freundlichen Grüßen,
    Thunderbolt
    Using heißt nur, dass am Ende des Using-Blocks Dispose aufgerufen wird -> deshalb funktioniert using auch nur wenn der Typ des Objektes IDisposable implementiert.
    Kurz um: Der Code ist nichts anderes als dies:

    VB.NET-Quellcode

    1. For i As Integer = 1 To Dateiliste.Length - 1
    2. Dim img As Image = Image.FromFile(Dateiliste(i))
    3. If img.Size = bsize Then
    4. ReDim Preserve Liste(ind)
    5. Liste(ind) = Dateiliste(i)
    6. ind += 1
    7. End If
    8. img.Dispose() ' Ressourcen freigeben!!! -> Wird speicher nicht freigegeben -> hoher Speicherverbrauch --> Prozess verbraucht mehr Speicher als ihm zusteht --> OutOfMemory
    9. Next


    Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.

    timmi31061 schrieb:

    VB.NET-Quellcode

    1. ReDim Preserve Liste(ind)
    Das ist Crap, mach da eine New List(Of ...) draus!
    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!

    RodFromGermany schrieb:

    Das ist Crap, mach da eine New List(Of ...) draus
    Ok, wieder was gelernt, habs jetzt so:

    VB.NET-Quellcode

    1. Dim Liste As New List(Of String)()
    2. For i As Integer = 1 To Dateiliste.Length - 1
    3. Using img As Image = Image.FromFile(Dateiliste(i))
    4. If img.Size.Equals(bsize) Then
    5. Liste.Add(Dateiliste(i))
    6. End If
    7. End Using
    8. Next
    9. ...
    10. *rödel**rödel*rödel**
    11. *Lüftersumm**
    12. ...
    13. Liste.Clear()

    In der Hoffnung das es nicht nur zufällig funktioniert und .Length eines String() das gleiche ist wie .Count einer Liste.
    Danke nochmal, Gruss, emil :)

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „emil.quast“ ()

    @LaMiy
    Also Fro Each müsste eigentlich langsamer sein.Bei For i wird einfach nur über einen Arrayindex direkt auf den Speicher zugegriffen, bei For Each wird bei jeder Iteration Enumerator.MoveNext und Enumerator.Current aufgerufen. Ich glaube aber nicht, dass der Unterschied Einfluss auf das Programm hat.
    Wenn du dich ein bisschen mit C++ auskennst wird ganz klar deutlich, warum For schneller sein muss. In .Net ist ein Array etwas abstraktes, eben eine Klasse. Im lowlevel gesehen ist ein Array aber nichts anderes, als ein Pointer. Laut der Pointeraritmethik wird die Adresse eines Pointers bei + und - immer um die Größe der darin gespeicherten Objekte verändert. Wenn du also einen Pointer auf einen Integer hast und +1 rechnest, dann erhöht sich die Adresse um 4 Bytes. Das ist im Prinzip genau das, was ein Array macht. Es wird die Startadresse gespeichert und dann immer um die gewünschte Anzahl Objekte erhöht. In C++ könnte man also *(p+1) schreiben, um das zweite Objekt eines Arrays anzusprechen. Da das aber sehr unbequem ist, hat man die Klammer-Schreibweise erfunden. Demnach ist p[1] absolut identisch zu der vorherigen Lösung. Und wenn man jetzt betrachtet, dass Pointerzugriff die schnellste Möglichkeit ist, an einem Computer auf Speicher zuzugreifen, dann ist auch klar, dass die For-Schleife schneller ist. Die Inkrementierung von i wird auch meist direkt in den Registern durchgeführt, was nochmals Performance geben sollte.
    Übrigens ist Using nicht nur sowas:

    VB.NET-Quellcode

    1. Dim Resource = ...
    2. 'Code...
    3. Resource.Dispose()

    sondern sowas:

    VB.NET-Quellcode

    1. Dim Resource As ResourceType
    2. Try
    3. Resource = ...
    4. 'Code...
    5. Finally
    6. Resouce.Dispose()
    7. End Try
    (Wobei die Resource-Variable natürlich nach dem Using-Block nicht mehr verfügbar ist.)
    Dadurch wird sichergestellt, dass auch im Falle einer auftretenden Exception die Ressource trotzdem verworfen wird.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils