Byte Array // Memory

  • VB.NET

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

    Byte Array // Memory

    Moin,
    ich lade in ein Byte Array eine Datei rein verarbeite diese dann und möchte am Schluss das Array wieder leeren und den RAM dazu wieder freigeben.
    Leider finde ich nichts was mir weiterhelfen könnte.

    Quellcode

    1. Dim bArr() as Byte
    2. bArr = IO.File.ReadAllBytes(txbInput.Text)


    Das Problem ist halt bei >1GB Dateien, da diese halt auf den RAM gehen..
    Falls jemand noch eine andere Lösung hat, wie ich die Datei erst gar nicht den RAM laden müsste, kann man das hier ebenfalls reinschreiben.

    corev7 schrieb:

    den RAM dazu wieder freigeben.
    Dafür gibt es in .Net den Garbage Collector. Er räumt mit der Zeit alles auf, dass keinen Verweis mehr hat. Lokale variablen verschwinden am Ende der Methode, bei Feldern oder Eigenschaften kannst du den Wert einfach auf "Nothing" setzen bArr = Nothing.

    Wenn du die Datei nicht auf einmal einlesen willst kannst du beispielsweise einen StreamReader drauf legen und Zeile für Zeile lesen und verarbeiten.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Bluespide“ ()

    Abhängig davon, wie die Berechnung aussieht, kannst Du Streams verwenden. Da lädst Du immer nur einen kleinen Teil der Datei in den RAM, verarbeitest den, lädst den nächsten Teil, verarbeitest den, und so weiter.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    @corev7 Interessant wird es dann, wenn Du mit New Instanzen von Klassen anlegst, die IDisposable implementieren.
    Diese musst Du mit .Dispose() wegräumen oder in einem Using-Block verwenden.
    Wenn nicht, sammeln sich "Leichen" im RAM an, die erst zum Laufzeitende weggeräumt werden und die Dir das Leben sehr schwer machen können, z.B. viele große Bilder gleichteitig.
    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!
    Hi
    das mit den "Leichen" stimmt so nicht. Der Finalizer wird von der GC trotzdem augerufen, sofern keine Instanzen darauf gehalten werden. Und der Finalizer ruft dann halt Dispose ggf. auf.
    (Randnotiz für die Unwissenden: IDisposable ist eine Schnittstelle, die man implementieren sollte, wenn etwas löschbar sein soll (ergibt sich weiter unten nochmal klarer). Der "Finalizer" ist in VB.Net als eine Methode "Finalize" erreichbar, die überschreibbar, d.h. Overridable, ist und von der Garbage Collection aufgerufen wird, wenn ein Objekt nicht mehr verwendet wird. Die Garbage Collection kümmert sich wiederum darum, dass Objekte gelöscht werden, die "lose" herumliegen, d.h. auf die nicht mehr zugegriffen werden kann, da innerhalb des Programms keine Möglichkeit mehr existiert, an die Ressource heranzukommen)

    Das Disposable-Pattern besteht im Prinzip aus folgendem:

    VB.NET-Quellcode

    1. Public Class SomeDisposableClass
    2. Implements IDisposable
    3. 'Private _disposed As Boolean
    4. Protected Overridable Sub Dispose(disposing As Boolean)
    5. '_disposed = True
    6. End Sub
    7. Public Sub Dispose() Implements IDisposable.Dispose
    8. Dispose(true)
    9. GC.SuppresFinalize(Me)
    10. End Sub


    Wobei in der Regel Dispose(Boolean) eine auf der Klasse deklarierte Boolean-Variable setzt, sodass nur einmal Dispose aufgerufen wird.
    In Dispose(Boolean) werden dann alle Ressourcen freigegeben, d.h. ggf. Dispose auf den privaten Variablen aufgerufen (Dispose darf nicht aufgerufen werden, wenn der Wert der Variablen auch ohne die SomeDisposableClass gültig ist), Referenztyp-Variablen auf Nothing gesetzt und unverwaltete Ressourcen freigegeben.
    Unverwaltete Ressourcen sind in der Regel Zeiger oder Rückgabewerte, die eine bestimmte Ressource identifizieren (es gibt dann idR. eine alloziierende und eine freigebende Methode, die aufgerufen werden muss).

    Werden unverwaltete Ressourcen verwendet, muss die Finalize-Methode überschrieben werden und die Ressourcen freigegeben werden, d.h. in unserem Fall Dispose(False) aufgerufen werden. Der Parameter disposing gibt also an, ob Finalize oder Dispose aufgerufen wurde. Im Finalize ist es also nicht notwendig, Referenzen von verwalteten Typen zu löschen.

    Da das auch auf Image zutrifft (Finalizer, Image.Dispose), wird Dispose auch so aufgerufen, aber halt, wenn die GC meint, es sei mal wieder fällig.

    Viele Grüße
    ~blaze~

    ~blaze~ schrieb:

    Der Finalizer wird von der GC trotzdem augerufen, sofern keine Instanzen darauf gehalten werden.


    Ja, aber!
    Es gibt keine Garantie, dass das auch wirklich passiert. Und gerade bei Bitmaps ist der Arbeitsspeicher schnell ausgeschöpft, ohne dass der GC einschreitet. Siehe dazu Was sind verwaltete / nicht verwaltete Ressourcen
    Generell gilt: Alles was IDisposable implementiert muss auch Disposed werden
    Eine Instanz eines Typen, der IDisposable implementiert, deren Finalizer aber aufgerufen wird, ist eigentlich immer ein Bug. Irgendwo hat man die Instanz verwaisen lassen, ohne vorher Dispose aufzurufen, denn hätte man das gemacht (und es richtig implementiert, mit GC.SuppressFinalization(Me)), wäre der Finalizer nie aufgerufen worden.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils

    ~blaze~ schrieb:

    das mit den "Leichen" stimmt so nicht.
    Ich hab das ausprobiert, ein Dispose() und ein Finalize(), mach da je ne MessageBox rein und los (C / C++-Wrapper).
    @corev7 Für Dich bedeutet das, aufzupassen und die Objekte zu zerstören.
    Wenn die IDisposable-Instanz nur lokal in einer Prozedur benötigt wird, arbeitest Du mit einem Using, da ist alles gegessen:

    VB.NET-Quellcode

    1. Using dlg = New Form2()
    2. dlg.ShowDialog()
    3. End Using
    Wenn die IDisposable-Instanz an die Laufzeit Deiner Klasse gebunden ist, leitest Du die Klasse am besten selbst von IDisposable ab und zerstörst alle klassen-internen Instanzen von IDisposable in der Dispose()-Prozedur.
    Die letzte Klasse, die eine solche Instanz zerstören muss, ist die MainForm, die hat bereits eine Dispose()-Prozedur.
    Das könnte dann so aussehen (die Region "IDisposable Support" wurde vom Designer angelegt, ich hab die Kommentare gelöscht):
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class MyClass
    2. Implements IDisposable
    3. Private br As Brush
    4. Private pen As Pen
    5. Public Sub New()
    6. Me.br = New SolidBrush(Color.Black)
    7. Me.pen = New Pen(Color.Red)
    8. End Sub
    9. Public Sub DoFoo()
    10. ' was mit be und pen tun
    11. End Sub
    12. #Region "IDisposable Support"
    13. Private IsDisposed As Boolean
    14. Protected Overridable Sub Dispose(disposing As Boolean)
    15. If Not Me.IsDisposed Then
    16. If disposing Then
    17. Me.br.Dispose()
    18. Me.pen.Dispose()
    19. End If
    20. End If
    21. Me.IsDisposed = True
    22. End Sub
    23. Public Sub Dispose() Implements IDisposable.Dispose
    24. Dispose(True)
    25. GC.SuppressFinalize(Me)
    26. End Sub
    27. #End Region
    28. End Class
    Wenn Du Dir nicht sicher bist, ob Du noch Instanzen am Laufen hast, lass über Deinen Code eine Codeanalyse laufen: Projekt -> Eigenschaften -> Codeanalyse. Kann aber sein, dass dieses Feature bei Deinem Studio nicht enthalten ist, ich hab Ultimate.
    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!

    corev7 schrieb:

    ...und möchte am Schluss das Array wieder leeren und den RAM dazu wieder freigeben.
    Dazu musst und solltest du gar nichts machen. Ein Array aufzuräumen, was nicht mehr gebraucht wird (auf das nicht mehr verwiesen ist) - daum kümmert sich der GarbageCollector.
    Allenthalben wird vom expliziten Aufruf von GC.Collect() abgeraten, denn das bringt die Aufräum-Strategie des GCs durcheinander, und verlangsamt dadurch das System.
    Lass den GC aufräumen, wann er es für richtig hält.
    Und wie gesagt: Nix tun, auch nicht disposen - Array ist nicht disposable.

    Aber dein wirkliches Problem ist ja das hier:

    VB.NET-Quellcode

    1. Dim bArr() as Byte
    2. bArr = IO.File.ReadAllBytes(txbInput.Text)
    So sollte man nur mit kleinen Dateien verfahren - keinesfalls mit Gigabyte-Brocken.

    Für große Dateien nehme man die Stream-Technologie, und verarbeite die Daten häppchenweise: Streams

    ErfinderDesRades schrieb:

    daum kümmert sich der GarbageCollector.
    Jou.
    Die MSDN empfiehlt, das Array auf Nothing | null zu setzen, wenn es nicht mehr benötigt wird.
    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:

    Ich hab das ausprobiert, ein Dispose() und ein Finalize(), mach da je ne MessageBox rein und los (C / C++-Wrapper).


    Wie gesagt, der Finalizer löscht die Bitmap. Die Frage ist viel eher, ob der Finalizer auch aufgerufen wird. Das habe ich nämlich nicht nachgeschaut. Wenn intern z.B. eine (nicht schwache) Referenz auf die Bitmap gehalten wird, ist das vom Designer schlecht designt worden, da die GC dann natürlich nicht arbeiten wird.

    Viele Grüße
    ~blaze~
    @~blaze~ Es ist schon eine Weile her. Das war ein Mehrkamerasystem mit ca. 30 FPS und nem Haufen Bildverarbeitung in mehreren Threads, und der Speicher lief voll.
    Da musste an der richtigen Stelle ein GC.Collect() rein, damit es lief, und in diesem Zusammenhang hab ich da mit dem Finalizer() ohne Dispose() aufzurufen ein paar Tests mit MsgBox gemacht. Die kamen tatsächlich erst bei Beendigung des Programms.
    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!
    Ich weiß schon, ich hab' die Tests ja auch mal irgendwann gemacht. Aber der Finalizer wird nunmal eigentlich von der GC aufgerufen.

    ~blaze~ schrieb:

    das mit den "Leichen" stimmt so nicht.

    bezieht sich darauf, dass, wird der Finalizer aufgerufen, die Ressourcen freigegeben werden. Wenn er das nicht wird, werden sie folglich auch nicht freigegeben. Wie gesagt, vermutlich ist es einfach schlecht designt oder vielleicht hat es ja auch nur irgendeinen anderen Grund (ich vermute ersteres).

    ~blaze~ schrieb:

    Das habe ich nämlich nicht nachgeschaut

    bezog sich übrigens darauf, ob im Code weitere Referenzen auf die Bitmap gehalten werden.

    Viele Grüße
    ~blaze~
    Ich verlinke nochmal auf Was sind verwaltete / nicht verwaltete Ressourcen
    Der GC sieht bei Bitmap-Instanzen nur wenige Duzend Bytes. Von den 100*n MB, die im unverwalteten Speicher liegen, weiß der GC nichts. Deshalb haben die Bitmap-Objekte auch eine relativ geringe Priorität beim Aufräumen.
    Dass GC.Collect() beim Debuggen keinen Effekt hat, konnte ich hier auch schon mal beobachten: C# Memory leak
    Also wie üblich gilt: Alles, was IDisposable implementiert, muss auch Disposed werden. Dann bekommt man solche Probleme auch normalerweise nicht.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils