MemLeak in For Each-Schleife

  • VB.NET

Es gibt 25 Antworten in diesem Thema. Der letzte Beitrag () ist von picoflop.

    MemLeak in For Each-Schleife

    Hallo,

    im TaskManager habe ich festgestellt das meine Anwendung jede Sekunde ein Stückchen (100-500KB) mehr RAM belegt, was nach einer gewissen Zeit ja problematisch werden könnte (?).

    Timer.Tick:

    VB.NET-Quellcode

    1. Private Sub update_ss()
    2. If is_bf3_running() = True Then
    3. tsslbl_bf3_running.Visible = True
    4. bol_errors = True
    5. Else
    6. tsslbl_bf3_running.Visible = False
    7. bol_errors = False
    8. End If
    9. If bol_errors = True Then
    10. Height = 340
    11. ss.Visible = True
    12. Else
    13. Height = 315
    14. ss.Visible = False
    15. End If
    16. End Sub

    Aufgerufene Funktion (Problem scheint hier zu sein):

    VB.NET-Quellcode

    1. Public Function is_bf3_running()
    2. For Each process In System.Diagnostics.Process.GetProcesses()
    3. If process.ProcessName.StartsWith("bf3") Then
    4. Return True
    5. End If
    6. Next
    7. Return False
    8. End Function


    Liegt das Problem an der For Each-Schleife? Falls ja, wie kann ich es umgehen?
    Es könnte an GetProcesses liegen, aber ich finde die Vorgehensweise schlecht und würde das auf jeden Fall ändern. Wenn BF mal läuft, dann kannst du den Prozess auch weiterhin tracken und muss nicht erst rausfinden ob er schon läuft. Wenn der getrackt wurde kannst du ja abfragen, ob der Prozess geschlossen wurde.
    @picoflop:
    "Danke, also ich muss mir keine Sorgen machen, das wird mit der Zeit immer wieder freigegeben?

    Gibt es eine Möglichkeit diesen Vorgang zu managen, zu kontrollieren?"


    @Mangafreak1995:
    "Die Vorgehensweise war mir nicht bekannt, ich werde mal danach googlen und wenn möglich umstellen."

    haiyyu schrieb:

    Process implementiert IDisposable.
    Daher:

    VB.NET-Quellcode

    1. process.Dispose()
    Sollten IDisposables in For Each Schleifen nicht disposed werden ?

    @ Dasive: du googlest nicht, da das zu speziell ist. Sobald deine Programme laufen packst du den Prozess einfach in eine eigene Prozessliste und überwachst die Prozessliste. Ist dein Prozess schon in der Liste brauchst du nicht mehr via getProcesses nach diesem suchen. Spart etwas an allem.
    @Mangafreak1995:
    "Hab zwar gegoogelt, aber nicht wirklich was gefunden.
    Du meinst ich lad alle Prozesse mit getProcesses und..wie ich verstehs nicht ganz.
    Falls du meinst ich schau dann eifach alle Prozesse durch und wen der gesuchte dabei ist ist Return = True, wie könnte ich dann sichergehen das der Prozess in der Zwischenzeit nicht beendet wurde?"
    Für jeden Process den Du überwachen willst, so
    - schmeisst Du den in eine Überwachungsliste List( of Process )
    - setzt dessen Eigenschaft EnableRaisingEvent auf true
    - setzt mit dem AddHandler Statement für das Event Process.Exited einen Zeiger auf eine Eventhandler Methode

    Dann bekommst Du in dieser Methode immer mit wenn sich ein Prozess aus Deiner Liste beendet.
    Kommst Du mit der Beschreibung zurecht ?

    @haiyyu warum sollte process.dispose durchgeführt werden solange der Process nirgendwo persistent ist , noch Events abonniert sind ?

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

    Man merkt wohl dass ich ziemlich unerfahren bin^^


    Danke für die ausführliche Beschreibung, konnte anfangs nichts damit anfangen und googeln hab ich jetzt aufgegeben.
    Ich glaube ich lass es einfach so wie es ist, es funktioniert das reicht mal für den Moment.
    Schade Kangaroo hat meine Idee gut ausgeführt und es wäre ein guter Weg IHMO gewesen.

    Mangafreak1995 schrieb:

    chade Kangaroo hat meine Idee gut ausgeführt und es wäre ein guter Weg IHMO gewesen.

    Danke Manga für das Lob, immer wieder gerne ... ;)

    VB.NET-Quellcode

    1. Public Class Form1
    2. Dim myProcessList As New List(Of Process)
    3. Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    4. ' wir starten der Einfachheit halber notepad selber
    5. Dim p As Process = Process.Start("notepad")
    6. ' in überwachungsliste schmeissen
    7. myProcessList.Add(p)
    8. ' events aktivieren
    9. p.EnableRaisingEvents = True
    10. ' Eventhandler setzen
    11. AddHandler p.Exited, AddressOf p_Exited
    12. End Sub
    13. ' wird beim Schliessen von Notepad aufgerufen
    14. Private Sub p_Exited(ByVal sender As Object, ByVal e As System.EventArgs)
    15. ' welcher process wars nochmal ?
    16. Dim p As Process = DirectCast(sender, Process)
    17. ' messagebox
    18. MessageBox.Show("Folgender Process wurde gestoppt: " & p.StartInfo.FileName)
    19. ' aus Liste austragen
    20. myProcessList.Remove(p)
    21. End Sub
    22. End Class

    Kangaroo schrieb:

    Für jeden Process den Du überwachen willst, so
    - schmeisst Du den in eine Überwachungsliste List( of Process )
    - setzt dessen Eigenschaft EnableRaisingEvent auf true
    - setzt mit dem AddHandler Statement für das Event Process.Exited einen Zeiger auf eine Eventhandler Methode

    Dann bekommst Du in dieser Methode immer mit wenn sich ein Prozess aus Deiner Liste beendet.
    Kommst Du mit der Beschreibung zurecht ?

    @haiyyu warum sollte process.dispose durchgeführt werden solange der Process nirgendwo persistent ist , noch Events abonniert sind ?
    Sonst ließe sich das Memory-Leak schlecht erklären, oder? Oder sind wir schon woanders?
    Wir sind eig. noch beim Memoryleak, aber mich nervte der Timer und das haben wir nun davon.

    haiyyu schrieb:

    Sonst ließe sich das Memory-Leak schlecht erklären, oder? Oder sind wir schon woanders?

    War eine offene Frage: ich denke die Garbage Collection hatte einfach noch nicht zugeschlagen, deshalb waren sie noch nicht abgeräumt. Aber Processes sollten eigentlich Managed Resourcen sein und damit beim nächsten GC Lauf verschwinden.

    Könnte man nachprüfen über Aufruf von GC.Collect ...
    Ähm - ich dachte, eine Klasse, die IDisposable implementiert, sollte immer disposed werden, wenn sie nicht mehr gebraucht wird.
    Ich sehe keinen Grund, davon eine Ausnahme zu machen.
    Falls Process Resourcen freizugeben hat, kanns die im Dispose freigeben, und andernfalls tuts auch keinem weh.
    @haiyyu Das Memory Leak ist wohl wirklich nur auf die Garbage-Collection zurückzuführen:

    Brainfuck-Quellcode

    1. memory Used before Loop: 384,040 Dispose: False
    2. memory Used after Collect: 384,224 Dispose: False
    3. --------------
    4. memory Used before Loop: 384,248 Dispose: True
    5. memory Used after Collect: 384,224 Dispose: True


    TestRoutine

    VB.NET-Quellcode

    1. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    2. Debug.Print(Environment.NewLine)
    3. ' cleanup
    4. GC.Collect()
    5. Debug.Print("memory Used before Loop: {0:n0} Dispose: {1}", GC.GetTotalMemory(True), False)
    6. MakeGarbage(False)
    7. GC.Collect()
    8. ' look at memory
    9. Debug.Print("memory Used after Collect: {0:n0} Dispose: {1}", GC.GetTotalMemory(True), False)
    10. Debug.Print("--------------")
    11. ' cleanup
    12. GC.Collect()
    13. Debug.Print("memory Used before Loop: {0:n0} Dispose: {1}", GC.GetTotalMemory(True), True)
    14. MakeGarbage(True)
    15. ' look at memory
    16. Debug.Print("memory Used after Collect: {0:n0} Dispose: {1}", GC.GetTotalMemory(True), True)
    17. End Sub
    18. Sub MakeGarbage(ByVal disposeProcess As Boolean)
    19. ' make garbage
    20. For Each p In Process.GetProcesses
    21. ' nix
    22. If disposeProcess Then p.Dispose()
    23. Next
    24. End Sub