Reservierten Arbeitsspeicher freigeben

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 16 Antworten in diesem Thema. Der letzte Beitrag () ist von Bolle.

    Reservierten Arbeitsspeicher freigeben

    Hallo und frohe Weihnachten zusammen,

    aktuell arbeite ich an einem Projekt, dass relativ komplex ist und verschiedenen Labels und TextBoxen auf verschiedenen Formen über Invoke aus unterschiedlichen Threads füllt. Das klappt soweit auch ganz gut, nur leider steigt der Arbeitsspeicher innerhalb von 48 Stunden stark bzw. bis zum Absturz an. Mittlerweile wird der Arbeitsspeicher im Taskmanager über den GC wieder ordnungsgemäß aufgeräumt, aber der reservierte Arbeitsspeicher bleibt erhalten. Wie kann ich diesen auch leeren?

    Bsp.:
    Arbeitsspeicherauslastung: 15,876 K ReservierterArbeitsspeicher: 649.180 K


    Vielen Dank und schöne Feiertage noch.
    Also wenn der reservierte Arbeitsspeicher kontinuierlich ansteigt, dann wird offensichtlich nicht "über den GC ordnungsgemäss aufgeräumt".
    Sondern du hast ein Memory-Leak.
    Zu leeren gibts da garnix, sondern du musst den code suchen, der das Memory-Leak verursacht, und durch saubere, leak-freie Programmierung ersetzen.
    @ErfinderDesRades Jou.
    @Bolle Es sind nicht TextBoxen und Label, sondern eher Bitmaps und Aufrufe in native DLLs, die Leaks verursachen.
    Identifiziere die, indem Du jeden Typ von Aufruf einzeln mehrere 1000 Mal aufrufst und dabei den Speixcher überwachst.
    Kann es sein, dass Du IDisposable-Instanzen nicht mit Disopose() aufräumst?
    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!
    Hatte auch erst Access Zugriffe, die ich in eine selbst geschriebenen DLL gepackt habe unter verdacht gehabt. Konnte den Speicheranstieg allerdings nicht in einem kleineren Projekt reproduzieren. Beim ersten Zugriff stieg der Speicher einmalig an und danach konnte unzählige weitere Zugriffe keinen Speicheranstieg erzeugen.
    Logdatei werden intern im Projekt verwaltet und am Ende wieder mit

    VB.NET-Quellcode

    1. Dim oStream As IO.StreamWriter = System.IO.File.AppendText(Logfilename)
    2. oStream.WriteLine(Message)
    3. oStream.Close()
    4. oStream.Dispose()


    geschlossen und freigegeben.

    Können Try and Catch eigentlich auch zu einem Speicheranstieg führen? Ich meine muss ich den ex irgendwie löschen/ lerren?

    Des Weiteren verwenden ich für die ankommenden TCPIP Telegramme

    VB.NET-Quellcode

    1. Me.Invoke(New TextboxAktual(AddressOf EmpfangeneNachrichten), tmp) 'Nachricht für die Ausführung


    und diese werden dann auf einem Label einer anderen Form angezeigt

    VB.NET-Quellcode

    1. Form1.Label10.Text = "Status A"


    :?: Fragen über Fragen :?:

    ErfinderDesRades schrieb:

    Also wenn der reservierte Arbeitsspeicher kontinuierlich ansteigt, dann wird offensichtlich nicht "über den GC ordnungsgemäss aufgeräumt".Sondern du hast ein Memory-Leak.
    Das glaube ich nicht unbedingt, denn hier ist nicht der normale Arbeitsspeicher-verbrauch (Privat Working Set) gemeint, sondern der Reservierte.

    Bolle schrieb:

    Arbeitsspeicherauslastung: 15,876 K
    Ist doch super.

    Bolle schrieb:

    ReservierterArbeitsspeicher: 649.180 K
    Der reservierter Arbeitsspeicher sollte dich eigentlich nicht interessieren. Den kannst du glaube ich eh nicht wirklich beeinflussen. Das kommt von Betriebssystem und ist nicht der Speicher, der in Benutzung ist. Als Beispiel: Chrome hat bei mir 2.141.002.980 K reservierten Arbeitsspeicher. Ja 2,1 Terra Byte.
    Leider fängt die Arbeitsspeicherauslastung nach einer gewissen Zeit wieder kurz unter dem reservierten Arbeitsspeicher an bis der cg kommt und wieder freigibt. Der reservierte Arbeitsspeicher steigt dabei wieder leicht an und alles fängt von vorne an. Ich denke Memory leak ist schon der richtige Ansatz.
    @Bolle Kannst Du mal das Projekt posten oder ein abgespecktes Projekt, das den Effekt reproduziert?
    Und dies genügt:

    VB.NET-Quellcode

    1. System.IO.File.AppendAllText(message & Environment.NewLine)
    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!

    Bolle schrieb:

    Können Try and Catch eigentlich auch zu einem Speicheranstieg führen? Ich meine muss ich den ex irgendwie löschen/ lerren?

    Ja, aber nicht wie Du denkst.
    Wenn Du zum Beispiel sowas machst:

    VB.NET-Quellcode

    1. Sub TestStuff()
    2. Try
    3. Dim Foo As New Bitmap(...)
    4. 'Irgendwas machen
    5. Foo.Dispose
    6. Catch ex As Exception
    7. 'Pokemon-ExceptionHandling
    8. End Try
    9. End Sub

    Wenn bei "Irgendwas machen" eine Exception ausgelöst wird, dann wird Foo.Dispose nie aufgerufen und die Bitmap bleibt im RAM.
    Einfach überall Try-Catch hinschreiben ist also keine gute Idee. Man sollte möglichst kleine Bereiche abdecken und möglichst spezifische Exceptions abfangen. Erst dann kommt dazu, dass man für IDisposable Using verwenden soll.

    Eventuell auch relevant: Was sind verwaltete / nicht verwaltete Ressourcen
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    @Niko Ortner @Bolle Für solche Konstrukter gibt es noch ein Finally:

    VB.NET-Quellcode

    1. Sub TestStuff()
    2. Dim Foo As Bitmap = Nothing
    3. Try
    4. Foo = New Bitmap(...)
    5. 'Irgendwas machen
    6. Catch ex As Exception
    7. 'Pokemon-ExceptionHandling
    8. Finally
    9. If Foo IsNot Nothing Then
    10. Foo.Dispose
    11. End If
    12. End Try
    13. End Sub
    Das sorgt da für Ordnung.
    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
    Ja, bzw. Using als Weiterführung davon. Aber mir ging's primär darum, zu zeigen, dass Exceptions Aufräumcode potenziell überspringen können.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    Ich denke die Try and Catch Abfrage ist somit ausgeschlossen. Kann Vielleicht jemand über meine Empfangroutine schauen, fehlt hier das dispose für den streamr oder streamw?
    Zudem habe ich noch ein paar my.settings. verbaut, die sich im Code ändern.



    VB.NET-Quellcode

    1. Public Sub ListenToConnection(ByVal con1 As Object)
    2. Dim con As Connection = DirectCast(con1, Connection)
    3. Do
    4. Try
    5. Dim tmp As String = con.streamr.ReadLine
    6. If String.IsNullOrEmpty(tmp) Or tmp Is Nothing Then 'Falls sich der Client verabschiedet
    7. Me.Invoke(New LabelAktual(AddressOf LabelaktualisierenServer), "Nicht verbunden")
    8. list.Remove(con)
    9. con.stream.Close()
    10. con.streamr.Close()
    11. con.streamw.Close()
    12. Me.Invoke(New TextboxAktual(AddressOf KnopfAusThreadAendern), "Button1Enable")
    13. Client.Dispose()
    14. Client.Close()
    15. Exit Do
    16. Else
    17. If AlteNachrichtEmpfangen = tmp Then
    18. GleicheNachrichtEmpfangen = GleicheNachrichtEmpfangen + 1
    19. If GleicheNachrichtEmpfangen > 10 Then
    20. AlteNachrichtEmpfangen = ""
    21. GleicheNachrichtEmpfangen = 0
    22. End If
    23. Else
    24. Me.Invoke(New LabelAktual(AddressOf LabelaktualisierenServer), "Verbunden")
    25. Me.Invoke(New TextboxAktual(AddressOf EmpfangeneNachrichtenAusfuehrung), tmp)
    26. Me.Invoke(New TextboxAktual(AddressOf EmpfangeneNachrichtenLOG), tmp)
    27. End If
    28. ' streamr.DiscardBufferedData()
    29. End If
    30. Catch ex As Exception 'Wichtig sonst wird ggf. nur eine Nachricht empfangen
    31. Try
    32. list.Remove(con)
    33. con.stream.Close()
    34. con.streamr.Close()
    35. con.streamw.Close()
    36. Me.Invoke(New LabelAktual(AddressOf LabelaktualisierenServer), "Nicht verbunden")
    37. Catch ex1 As Exception
    38. End Try
    39. Exit Do
    40. End Try
    41. Loop
    42. End Sub

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

    Kann es auch sein, dass ein Invoke wie unten im Beispiel die TextBox mit "unendlich" vielen Nachrichten füllt und diese vielleicht einfach nur ab einer Größe von x Zeilen einmal gelöscht werden müsste?

    VB.NET-Quellcode

    1. TextBox12.Invoke(New BefehlXYZ(AddressOf BefehleAusThread), "Auftrag1", Nachricht)


    Zudem wird die Debug und nicht die Release Version verwendet.

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

    Also ich habe gestern noch den Access Datenbankzugriff ein wenig reduziert. Es wird nur einmal geprüft ob ein Datensatz in einer Datenbank vorhanden ist und nicht wie früher die DLL mehrmals im Thread aufgerufen. Scheint etwas des Speicher zu reduzieren.
    Wird beim Aufruf der DLL immer eine neue Instanz für die Verbindung angelegt?

    Innerhalb der DLL:

    VB.NET-Quellcode

    1. Dim con1 As New OleDbConnection
    2. Dim cmd As New OleDbCommand

    Bolle schrieb:

    Wird beim Aufruf der DLL immer eine neue Instanz für die Verbindung angelegt?
    Lies dir die Doku des Schlüsselwortes New, und beantworte die Frage selbst.
    msdn.microsoft.com/de-de/library/dd409611(v=vs.140).aspx
    New richtig zu verstehen ist absolut Muss inne OOP.
    Zumal die Frage streng genommen keinen Sinn ergibt (also nur du selbst sicher wissen kannst, was mit der Frage gemeint ist), weil eine Dll kann man nicht aufrufen.
    Außerdem gibt es da noch solch Konstrukte wie CreateInstance(), wo das New implizit drinne steckt.
    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!
    Der Connection String in der DLL:

    VB.NET-Quellcode

    1. con1.ConnectionString =
    2. "Provider=Microsoft.Jet.OLEDB.4.0;" &
    3. "Data Source=" & PfadDatenbank


    und aus dem Projekt

    VB.NET-Quellcode

    1. 'Auf Form 1
    2. Public access As New MeineDll.Klassenname
    3. 'Auf Form 2
    4. Form1.access.Verbinden(PfadDatenbank, "Tabellenname")


    ErfinderDesRades schrieb:

    weil eine Dll kann man nicht aufrufen.


    Jetzt ist Weihnachten vorbei und wir räumen unsere Goldwaage wieder in den Schrank :) #Verweis

    Der Zugriff auf die DLL erfolgt auch aus verschiedenen Threads, dabei habe ich bis jetzt allerdings keine Leaks feststellen können.

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