Garbage Collector entsorgt noch benötigtes object

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

Es gibt 10 Antworten in diesem Thema. Der letzte Beitrag () ist von tonschnoer.

    Garbage Collector entsorgt noch benötigtes object

    Hallo, zusammen!

    Ich habe ein Problem mit einem Windows Dienst, den ich geschrieben habe:

    In der Hauptklasse des DIenstes wird eine Public Shared Collection angelegt, die eine Anzahl Objekte enthält (i.d.R. 4 Stück; ändern sich nur sehr selten).
    Die Objekte werden in einer Methode mit Dim vgc as VGClient instanziiert und dann der Collection hinzugefügt. Nach sehr langer Laufzeit (i.d.R. 3-4 Monate)
    kommt es dazu, dass plötzlich der Garbage Collector die Collection leert und das Collection object wegräumt. Ich merke das immer daran, dass, wenn ich
    versuche, einen Enumerator auf die Collection zu bekommen, eine NullPointer-Exception geworfen wird.

    Nun meine Frage(n):
    - kann das Verhalten daran liegen, dass nur ganz am Anfang Schreibzugiffe auf die Collection stattfinden und danach nur noch Lesezugriffe?
    - ist es ein Problem, ein mit Dim vgc as new VGClient insanziiertes Objekt einer Public Shared Collection hinzuzufügen? Spielt hier eventuell der Begriff "weak reference" eine Rolle?
    - kann ich auf andere Weise dem Garbage Collector sagen, dass die Collection mit 4 Objekten noch benötigt wird und er seine Finger davon lassen soll?

    Sourcecode stelle ich gerne zur Verfügung.

    VIelen Dank im Voraus!
    Der Kai

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

    tonschnoer schrieb:

    - kann das Verhalten daran liegen, dass nur ganz am Anfang Schreibzugiffe auf die Collection stattfinden und danach nur noch Lesezugriffe?
    Nein, der GC räumt nur Objekte ohne Referenzen auf. Egal ob du lesend, schreibend oder gar nicht mehr darauf zugreifst.


    tonschnoer schrieb:

    - ist es ein Problem, ein mit Dim vgc as new VGClient insanziiertes Objekt einer Public Shared Collection hinzuzufügen? Spielt hier eventuell der Begriff "weak reference" eine Rolle?
    Ja, genau das ist dein Problem: WeakReference Klasse
    Stellt einen schwachen Verweis dar. Dieser verweist auf ein Objekt, ohne jedoch dessen Freigabe durch die Garbage Collection zu verhindern.
    WeakReference ist genau der eine Sonderfall, bei dem du einen Verweis auf ein Objekt hast, aber dem GC sagst: Kannst du aufräumen wenn du willst, aber wenn es noch da sein sollten benutzt ich das vielleicht noch. Ich schätze das hast du bei dir hier falsch verwendet. Wenn du die Objekt noch brauchst, dann sind diese nicht mit WeakReference zu verweisen, sondern ganz normal.


    tonschnoer schrieb:

    - kann ich auf andere Weise dem Garbage Collector sagen, dass die Collection mit 4 Objekten noch benötigt wird und er seine Finger davon lassen soll?
    Benutz einfach keine WeakReference. Alle Objekte in einer normalen Liste bleiben so lange da, solange es die Liste an sich noch gibt, bzw. solange sich diese Objekte in der Liste befinden.
    Hi, und vielen Dank für die schnelle Antwort!

    Leider habe ich die WeakReference Klasse nicht explizit verwendet; ich befürchtete nur, ich hätte vielleicht eine Weak Reference erstellt, ohne es zu wissen.
    Die Objekte landen wie folgt in der Collection (stark gekürzt):

    VB.NET-Quellcode

    1. Class Main
    2. Public Shared clients As New Collection()
    3. '...
    4. Sub add_clients()
    5. dim vgc as VGClient = new VGClient()
    6. clients.add(vgc)
    7. End Sub
    8. End Class



    Nachdem add_clients() direkt nach dem Programmstart 1 Mal aufgerufen wird, wird es nie wieder aufgerufen. Jedoch wird in anderen Klassen sehr häufig lesend auf die Collection clients zugegriffen.
    Das geht wie beschrieben 3-4 Monate gut, bis plötzlich erst die Collection clients leer und dann = NULL ist.

    Würde es helfen, dim vgc as VGClient aus der Methode heraus zu den Klassenvariablen zu verschieben? Darf ich nicht erwarten, dass die erstellten vgc Objekte über die Collection clients außerhalb der
    Methode add_client() zugreifbar bleiben? Fragen über Fragen :) ...

    Viele Grüße
    Der Kai
    Vielleicht macht ja doch irgendjemand Blödsinn mit dem Ding.
    du könntest eine Readonly Property davon machen, und evtl. auch umsatteln auf eine ReadonlyCollection(Of VGClient)
    Wo ich das grad sehe: Collection ist doch ein völlig veralteter und vergammelter Datentyp, den man nie mehr benutzen sollte.
    Machma Visual Studio - Empfohlene Einstellungen
    und bring dein Proggi erstmal auf einen vertretbaren Stand.
    Hi, und danke für die Hinweise!

    Habe jetzt Option Strict On aktiviert; der Kollateralschaden hielt sich in Grenzen. Datentypen sind mir bewusst, den My-Namespace verwende ich nicht,
    und den Namespace Visual Basic brauchte ich auch nicht zu importieren. Nix von verwendet.

    Die ReadOnlyCollection passt leider nicht ganz auf meinen Anwendungsfall; ich war da etwas ungenau in der Beschreibung. In der Regel ändert sich
    lange Zeit nichts an der Collection, es muss aber auf jeden Fall möglich sein, nachträglich Elemente hinzuzufügen oder wegzunehmen.
    Du sprachst von Collection als veraltetem Datentyp; ich schau mich mal nach Alternativen um, oder hättest Du konkret einen Vorschlag? Ich bräuchte
    quasi nur einen "Sack voll Objekte", den ich durchsuchen kann... Irgendwas aus dem Collections.Generic Namespace (Dictionary o.ä.)?

    Beste Grüße und vielen Dank für die Hilfe!
    Der Kai

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

    Hallo, zusammen!

    Ich habe jetzt mal die Möglichkeiten gesichtet und im ersten Anlauf mich für ein Dictionary (Of String, VGClient) entschieden, da es zu den
    verschiedenen Programmteilen am besten passt. Ich bin gespannt, ob der Garbage Collector nun das Objekt in Ruhe lässt.

    VB.NET-Quellcode

    1. Class Main
    2. Public Shared clients As New Dictionary (Of String, VGClient)
    3. '...
    4. Sub add_clients()
    5. dim vgc as VGClient = new VGClient()
    6. clients.add(vgc.ID,vgc)
    7. End Sub
    8. End Class


    Bleibt noch die Frage, ob es sinnvoller ist, die Zeile "dim vgc as VGClient" aus dem Sub rauszunehmen und als globale Variable zu deklarieren. Was meint Ihr?

    Regards
    Kai
    Nun ja, je nach lesart. Mehrere Objekte der Klasse VGClient werden in diesem Sub erzeugt und in eine globale Collection "hineingeworfen"
    Sie werden während der gesamten Laufzeit des Programms benötigt und auch von anderen Klassen und Methoden referenziert.
    Liegt da eventuell der Pferdefuß - böser Designfehler?

    VG
    Der Kai
    mir klingt das nicht unbedingt als Designfehler.
    Also man vermeidet sowas eher, aber wenn mans braucht braucht mans halt.
    Die hochgepriesenen IO-Container zum Beispiel sind im Kern genau so etwas.

    Also imo: Am Design liegts nicht, anne Collection/Dictionary liegts auch nicht.
    VGClient klingt nach einer sehr komplexen Komponente - vielleicht hat die ja irgendwo einen verheerenden Bug, der sogar den GC veräppelt.
    Das wäre auf jeden Fall nicht schlecht, wenn man sowas vielleicht zumindest einmal am Tag auswechselt.
    Dann gibts noch Viren und instabile Windowse - du siehst: Ich bin Latein ziemlich Ende.



    Achso - was ist mit Post#4 - hast du die Zugreifbarkeit mal so weit wie möglich beschränkt?
    Also dass das Dictionary readonly ist (zumindest von aussen), und auch seine Elemente?
    Die Klasse VGClient ist eigentlich nur ein ganz einfaches Datengrab. Da passiert nicht viel:

    VB.NET-Quellcode

    1. Public Class VGClient
    2. Public Host As String
    3. Public Port As Integer
    4. Public Timestamp As DateTime
    5. Public master_state As String
    6. Public Type As String
    7. Public encrypt_connection As Boolean = False
    8. Public IP As System.Net.IPAddress
    9. Sub New()
    10. Me.Port = 3334
    11. End Sub
    12. End Class


    Das Dictionary habe ich ehrlich gesagt noch nicht readonly; da ich aber die ganze Anwendung alleine programmiere,
    kann ich ausschließen, dass in anderen Programmteilen schreibend auf das Dictionary zugegriffen wird. Es wird vielmehr
    nur über die Member iteriert:

    VB.NET-Quellcode

    1. ' ältere Programmversion, daher noch kein Dictionary:
    2. For Each VGC As VGClient In DHDVisualGateway2018.clients
    3. Send_Heartbeat(VGClient.IP, VGCLient.Port)
    4. Next


    Es gibt eine einzige Stelle im Programm, wo Mitglieder aus dem Dictionary entfernt werden:

    VB.NET-Quellcode

    1. For Each cl As VGClient In DHDVisualGateway2018.clients
    2. age = DateDiff(DateInterval.Minute, cl.Timestamp, Now)
    3. log.Debug(cl.Host & " : " & age.ToString() & " Minutes")
    4. If age > 10 Then
    5. log.Info("Long Time (>10 Minutes) no hear from InfoDisplayClient " & cl.Host & ". Removing client from list of active clients.")
    6. clients.Remove(cl.Host)
    7. End If
    8. Next


    Das Entfernen würde aber auch gelogged werden. Im Logging war aber zu der betreffenden Zeit nichts. Von der einen auf die andere Sekunde
    war die Collection 1. leer und 2. NULL. Das bringt mich darauf, dass es der Garbage Collector gewesen sein muss. Das Problem ist jetzt leider
    erstmals nach 3-4 Monaten Laufzeit des Programms aufgetreten und nicht aktiv reproduzierbar...

    Vielleicht wars auch mal wieder das McAfee ATP-Modul, das hat in der Vergangenheit immer mal wieder für einigen Spaß gesorgt...
    Ich habe jetzt vorsichtshalber mal die VGClients (sind nur maximal 10 Stück) als globales Objekt deklariert. Ich geh aber auch noch mal in mich, wie ich mit Deinem Vorschlag
    readonly umgehe. Als Workaround legt die Software, sobald sie das Fehlen des Dictionarys bemerkt, ein Dictionary neu an, und die Clients melden
    sich innerhalb von 1 Minute neu an. Der Fehler ist also nur noch zeitlich begrenzt. Trotzdem würde ich gerne der Problematik auf die Spur
    kommen, warum der GC da einfach Objekte wegräumt...

    Auf jeden Fall vielen Dank erstmal! Alle Meinungen are greatly appreciated!

    Gruß
    Der Kai