2010-09-09 1 views
13

Ich habe ein Problem mit Speicherverlusten in meiner .NET Windows-Dienstanwendung. Also habe ich begonnen, Artikel über Speicherverwaltung in .NET zu lesen. Und ich habe eine interessante Praxis in one of Jeffrey Richter articles gefunden. Dieser Übungsname ist "Objektauferstehung". Es sieht aus wie Code Situierung, die globale oder statische Variable initialisiert auf „this“:Verwendung von Objektauferstehung

protected override void Finalize() { 
    Application.ObjHolder = this; 
    GC.ReRegisterForFinalize(this); 
} 

Ich verstehe, dass dies eine schlechte Praxis ist jedoch würde Ich mag Muster wissen, dass diese Praxis verwendet. Wenn Sie etwas wissen, schreiben Sie bitte hier.

+3

Wenn ein Entwickler, der für mich arbeitet, diesen Code geschrieben hat, würde ich ihn herausreißen lassen. –

+0

@John: Ich stimme völlig zu - es ist wirklich etwas da für extreme Randfälle, IMO. –

+3

@John: Ich verstehe es klar und mein Interesse ist nur wissenschaftlich :) – Vokinneberg

Antwort

5

Spekulativ: In einer Pool-Situation, wie der ConnectionPool.

Sie können damit Objekte zurückfordern, die nicht ordnungsgemäß entsorgt wurden, für die der Anwendungscode jedoch keine Referenz mehr enthält. Sie können sie nicht in einer Liste im Pool speichern, da dies die GC-Sammlung blockieren würde.

+0

Ja, die erste Idee war ein gepooltes Objekt mit z. B. inneren Zeiten. Wenn der Zähler nicht mehr auf 0 steht, muss die Finalisierung unterdrückt werden und das Objekt stirbt. Aber ich denke sowieso, dass es keine gute Implementierung von Pooling ist. – Vokinneberg

+0

@Vokin, die Counter-Strategie ist nicht die einzige Möglichkeit, die Lebenszeit hier zu verwalten. Ich denke, der Hauptpunkt ist die Rückforderung einer Ressource aus dem GC. –

3

Der einzige Ort, an den ich denken könnte, wäre potenziell, wenn Sie versuchen würden, eine Ressource zu bereinigen, und die Ressourcenbereinigung fehlgeschlagen ist. Wenn es wichtig ist, den Bereinigungsvorgang erneut zu versuchen, können Sie das zu finalisierende Objekt "neu registrieren", was hoffentlich zum zweiten Mal gelingen würde.

Das gesagt, ich würde das in der Praxis ganz vermeiden.

7

Aus dem gleichen Artikel: "Es gibt sehr wenige gute Anwendungen von Auferstehung, und Sie sollten es wirklich vermeiden, wenn möglich."

Die beste Anwendung, die ich mir vorstellen kann, ist ein "Recycling" -Muster. Betrachten Sie eine Fabrik, die teure, praktisch unveränderliche Objekte produziert; zum Beispiel, Objekte, die durch Parsen einer Datendatei instanziiert werden, oder durch Reflektieren einer Assembly oder durch tiefes Kopieren eines "Master" -Objektgraphen. Die Ergebnisse werden sich wahrscheinlich nicht jedes Mal ändern, wenn Sie diesen teuren Prozess durchführen. Es ist in Ihrem Interesse, die Instanziierung von Grund auf zu vermeiden. Aus bestimmten Designgründen muss das System jedoch in der Lage sein, viele Instanzen (keine Singletons) zu erstellen, und Ihre Kunden können nichts über die Factory wissen, sodass sie das Objekt selbst "zurückgeben" können. Sie können das Objekt injizieren lassen oder einen Factory-Methoden-Delegaten erhalten, von dem sie eine Referenz erhalten. Wenn die abhängige Klasse den Gültigkeitsbereich verlässt, würde dies normalerweise auch für die Instanz gelten.

Eine mögliche Antwort besteht darin, Finalize() zu überschreiben, einen Teil des veränderlichen Zustands der Instanz zu bereinigen und dann, solange sich die Factory im Bereich befindet, die Instanz erneut an ein Mitglied der Factory anzuhängen. Dies ermöglicht, dass der Müllsammlungsprozess den wertvollen Teil dieser Objekte tatsächlich "recycelt", wenn sie sonst außer Reichweite und vollständig zerstört würden. Die Fabrik kann nachsehen und sehen, ob sie recycelte Objekte in ihrer "Tonne" zur Verfügung hat, und wenn ja, kann sie sie aufpolieren und verteilen. Die Fabrik müsste nur dann eine neue Kopie des Objekts instanziieren, wenn die Anzahl der von dem Prozess verwendeten Gesamtobjekte zunimmt.

Andere mögliche Anwendungen können eine hochspezialisierte Logger- oder Audit-Implementierung sein, bei der Objekte, die nach ihrem Tod verarbeitet werden sollen, an eine Arbeitswarteschlange angehängt werden, die von diesem Prozess verwaltet wird. Nachdem der Prozess sie behandelt hat, können sie vollständig zerstört werden.

Im Allgemeinen, wenn Sie wollen, dass Angehörige denken, dass sie ein Objekt loswerden, oder sich nicht kümmern müssen, aber Sie die Instanz behalten wollen, kann Auferstehung ein gutes Werkzeug sein, aber Sie müssen Beobachte SEHR sorgfältig, um Situationen zu vermeiden, in denen Objekte, die wiederbelebte Referenzen erhalten, zu "Pack-Ratten" werden und jede Instanz, die jemals erstellt wurde, für die gesamte Lebensdauer des Prozesses behalten.

+0

Object recycling könnte (und wahrscheinlich sollte) ohne Auferstehung durchgeführt werden. Ein größerer Anwendungsfall ist meiner Meinung nach, wenn ein Objekt eine Bereinigungsaktion hat, die nur durchgeführt werden sollte, wenn keine anderen Verweise darauf irgendwo im Universum existieren können (sogar innerhalb von Objekten mit Finalizern). Das zu bereinigende Objekt kann einen Verweis auf ein "saubereres" Objekt enthalten, das eine statische lange schwache Referenz auf das zu reinigende Objekt erzeugen kann. Der Finalizer des saubereren Objekts kann prüfen, ob die lange schwache Referenz gestorben ist; Wenn dies der Fall ist, sollte es die statische Referenz darauf zerstören und die Säuberung durchführen. – supercat

+0

Wenn die schwache Referenz nicht abgestorben ist, sollte das sauberere Objekt erneut registriert werden, um es zu finalisieren (es wird zumindest vorübergehend wieder belebt). Beachten Sie, dass die 'WeakReference' auch dann stirbt, wenn ihr Ziel noch am Leben war, wenn der einzige Hinweis auf die' WeakReference' in einem der Felder des Cleaner war. Der Cleaner muss eine statische verwurzelte Referenz auf die "WeakReference" erstellen, um sicherzustellen, dass er nur dann stirbt, wenn sein Ziel dies tut. – supercat

3

Ein Bruder von mir arbeitete einmal an einer leistungsstarken Simulationsplattform.Er erzählte mir, wie die Objektkonstruktion in der Anwendung ein nachweisbarer Engpass für die Anwendungsleistung war. Es scheint, dass die Objekte groß waren und eine gewisse Verarbeitung zur Initialisierung erforderten.

Sie haben ein Objekt-Repository implementiert, das "zurückgezogene" Objektinstanzen enthält. Bevor sie ein neues Objekt erstellen, prüfen sie zunächst, ob im Repository bereits existiert.

Der Kompromiss bestand in einer erhöhten Speicherauslastung (da möglicherweise viele nicht verwendete Objekte gleichzeitig vorhanden sind) für eine höhere Leistung (da die Gesamtzahl der Objektkonstruktionen reduziert wurde).

Beachten Sie, dass die Entscheidung, dieses Muster zu implementieren, auf den Engpässen beruhte, die sie bei der Profilerstellung in ihrem spezifischen Szenario feststellten. Ich würde erwarten, dass dies ein außergewöhnlicher Umstand ist.

0

Für was ich weiß .net ruft Finalizer in keiner bestimmten Reihenfolge. Wenn Ihre Klasse Verweise auf andere Objekte enthält, könnten sie beim Aufruf des Finalizers finalisiert (und somit disponiert) worden sein. Wenn Sie sich dann entscheiden, Ihr Objekt wieder auferstehen zu lassen, haben Sie Referenzen auf finalisierte/disponierte Objekte.

class A { 
    static Set<A> resurectedA = new Set<A>(); 
    B b = new B(); 
    ~A() { 
    //will not die. keep a reference in resurectedA. 
    resurectedA.Add(this); 
    GC.ReRegisterForFinalize(this); 

    //at this point you may have a problem. By resurrecting this you are resurrecting b and b's Finalize may have already been called. 
    } 
} 
class B : IDisposable { 
    //regular IDisposable/Destructor pattern http://msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.110).aspx 
}