Understanding garbage collection in .NET
Betrachten Sie den folgenden code:
public class Class1
{
public static int c;
~Class1()
{
c++;
}
}
public class Class2
{
public static void Main()
{
{
var c1=new Class1();
//c1=null; //If this line is not commented out, at the Console.WriteLine call, it prints 1.
}
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine(Class1.c); //prints 0
Console.Read();
}
}
Nun, obwohl die variable c1 in der main-Methode wird out-of-scope und nicht referenziert wird weiter von einem anderen Objekt, wenn GC.Collect()
genannt wird, warum wird es nicht fertiggestellt es?
Die GC nicht sofort frei-Instanzen, wenn Sie außerhalb des Gültigkeitsbereichs. Es tut dies, wenn er es für notwendig hält. Sie können Lesen Sie alles über die GC hier: msdn.microsoft.com/en-US/library/vstudio/0xy59wtx.aspx
Dein link ist kaputt.)
Dein link ist kaputt.)
InformationsquelleAutor Victor Mukherjee | 2013-06-16
Du musst angemeldet sein, um einen Kommentar abzugeben.
Werden Sie stolperte hier und Zeichnung sehr falschen Schlussfolgerungen, weil Sie einen debugger. Sie benötigen zum ausführen von code, wie es läuft auf Ihrem Computer eines Benutzers. Wechseln Sie zu der Freigabe erstellen mit Build + - Konfigurations-manager, ändern Sie die "Active solution configuration" - combo in der oberen linken Ecke auf "Freigeben". Als Nächstes gehen Sie in Tools + Options, Debugging, allgemein, und deaktivieren Sie die "Unterdrückung der JIT-Optimierung" - option.
Nun führen Sie das Programm erneut und basteln mit den source-code. Beachten Sie, wie die zusätzlichen Klammern haben überhaupt keine Wirkung. Und beachten Sie, wie wird die variable auf null macht überhaupt keinen Unterschied. Es wird immer drucken " "1". Es funktioniert jetzt auf die Weise, die Sie hoffen und erwarten, dass es funktioniert.
Die nicht verlassen, mit der Aufgabe, zu erklären, warum es funktioniert, so anders, wenn Sie ausführen die Debug-build. Das erfordert zu erklären, wie der garbage collector erkennt, lokale Variablen und wie die betroffenen durch eine debugger vorhanden.
First off, wird der jitter führt zwei wichtige Aufgaben, wenn es kompiliert den IL für eine Methode in Maschinencode. Die erste ist sehr sichtbar im debugger sehen Sie den Maschinen-code mit der Debug - + Windows + Disassembly-Fenster. Die zweite Aufgabe ist jedoch völlig unsichtbar. Es erzeugt auch eine Tabelle, die beschreibt, wie die lokalen Variablen innerhalb der Methode Körper verwendet werden. Die Tabelle hat einen Eintrag für jede Methode, die Argumente und lokalen Variablen, mit zwei Adressen. Die Adresse, wo die variable zuerst speichern eine Referenz auf ein Objekt. Und die Adresse von der Computer-code-Anweisung, die die variable nicht mehr verwendet. Auch, ob die variable gespeichert ist, auf den stack-frame oder ein cpu-register.
Diese Tabelle ist wichtig für den garbage collector, den es braucht, um zu wissen, wo zu suchen, Objekt Referenzen, wenn es ausführt, eine Sammlung. Ziemlich einfach zu tun, wenn die Referenz ist Teil eines Objekts auf der GC-heap. Definitiv nicht einfach zu tun, wenn die Objekt-Referenz gespeichert ist, in ein CPU-register. Die Tabelle sagt, wo Sie zu suchen.
"Nicht mehr verwendet" - Adresse in der Tabelle ist sehr wichtig. Es lässt den garbage collector sehr effiziente. Es sammeln kann, eine Referenz auf ein Objekt, selbst wenn Sie innerhalb einer Methode und die Methode noch nicht beendet noch. Das ist sehr üblich, Ihre Main () - Methode zum Beispiel wird immer nur nicht weiter ausgeführt, kurz bevor dein Programm beendet wird. Klar Sie würde nicht wollen, dass irgendwelche Objekt-Referenzen verwendet, die Main () - Methode, um live für die Dauer des Programms, das liefe auf ein Leck. Der jitter können die Tabelle verwenden, um zu entdecken, dass eine solche lokale variable ist nicht mehr nützlich sind, je nachdem, wie weit das Programm Fortgeschritten ist innen, die Main () - Methode auf, bevor es einen Anruf.
Eine fast Magische Methode, die im Zusammenhang mit dieser Tabelle ist GC.KeepAlive(). Es ist ein sehr Besondere Methode, es erzeugt keine Codes. Seine einzige Aufgabe ist, die Tabelle modifizieren. Es erstreckt die Lebensdauer der lokalen Variablen, verhindern, dass die Referenz speichert es immer Müll gesammelt. Die einzige Zeit, die Sie brauchen, um es zu verwenden zu stoppen, die GC zu über-eifrig mit dem sammeln einen Verweis, dass kann passieren, in interop-Szenarien, in denen eine Referenz übergeben wird, nicht verwalteten code. Der garbage collector kann nicht sehen, solche Hinweise verwendet werden, die von solchen code, da es nicht kompiliert von den jitter so nicht die Tabelle, die sagt, wo die Referenz. Vorbei an ein delegate-Objekt, an die nicht verwaltete Funktion wie EnumWindows() ist der boilerplate Beispiel, wenn Sie brauchen, um zu verwenden, GC.KeepAlive().
So, wie Sie sagen können, aus Ihrem Beispiel-snippet, nachdem Sie in der Release-build, lokale Variablen kann zusammengestellt bekommen früh, bevor die Methode beendet. Sogar noch mächtiger, ein Objekt bekommen kann, gesammelt während eine seiner Methoden ausführt, wenn diese Methode bezieht sich nicht länger auf diese. Es gibt ein problem mit diesem, ist es sehr umständlich zu Debuggen, die eine solche Methode. Da können Sie auch setzen Sie die variable im Watch-Fenster oder überprüfen Sie es. Und es würde verschwinden beim Debuggen, wenn ein GC-Auftritt. Das wäre sehr unangenehm, so ist der jitter bewusst, dass es ein debugger angehängt ist. Es dann ändert der Tabelle und ändert die "zuletzt verwendet" - Adresse. Und ändert es von seinem normalen Wert an die Adresse von die Letzte Anweisung in der Methode. Bleibt die variable erhalten, solange die Methode noch nicht zurückgegeben. Das ermöglicht es Ihnen, um zu beobachten, bis die Methode zurückkehrt.
Dies erklärt nun auch, was Sie zuvor gesehen haben und warum Sie die Frage gestellt. Es gibt "0", da der GC.Ruf sammeln kann, sammeln die Referenz. Die Tabelle sagt, dass die variable in Verwendung Vergangenheit der GC.Collect () - Aufruf, den ganzen Weg bis zum Ende der Methode. Gezwungen, so zu sagen, indem der debugger und durch ausführen der Debug-build.
Wird die variable auf null gesetzt wird, einen Effekt haben, jetzt, da die GC untersuchen Sie die variable, und nicht mehr eine Referenz. Aber stellen Sie sicher, dass Sie nicht fallen in die Falle, dass viele C# - Programmierer geraten sind, eigentlich schreiben, dass code war sinnlos. Es macht keinerlei Unterschied, ob oder nicht, die Aussage ist vorhanden, wenn Sie führen Sie den code in der Release-build. In der Tat, die jitter-Optimierer entfernen diese Aussage, da es keinerlei Auswirkungen. So sicher sein, nicht code schreiben, dass, obwohl es schien um einen Effekt zu haben.
Eine Letzte Anmerkung zu diesem Thema, dies ist, was die Programmierer in Schwierigkeiten, schreiben kleine Programme, die etwas tun, mit einer Office-app. Der debugger in der Regel wird Sie auf dem Falschen Weg, Sie wollen das Office-Programm beenden-on-demand. Der geeignete Weg, dies zu tun ist durch Aufruf von GC.Sammeln(). Aber Sie werden feststellen, dass es nicht funktioniert, wenn Sie Debuggen Sie Ihre app und führen Sie in never-never land durch Aufruf von Marshal.ReleaseComObject(). Die manuelle Speicherverwaltung, es funktioniert selten richtig, da Sie werden leicht übersehen, eine unsichtbare Referenz zur Benutzeroberfläche Referenz. GC.Collect() funktioniert tatsächlich, nur nicht, wenn Sie das Debuggen der app.
Ich habe gerade diese Super Erklärung, das beantwortet auch einen Teil meiner Frage hier: stackoverflow.com/questions/30529379/... über GC und thread-Synchronisation. Eine Frage habe ich noch: ich Frage mich, ob die GC tatsächlich komprimiert & updates-Adressen, die verwendet werden, in einem register (im Speicher gespeichert, während unterbrochen), oder springt Sie? Ein Prozess, der die Aktualisierung von Registern nach Aussetzung der thread (vor der Zusammenfassung) fühlt sich für mich wie eine ernsthafte Sicherheits-thread blockiert durch das OS.
Indirekt, ja. Der thread wird angehalten, die GC-updates die backing-Speicher für die CPU-Register. Mal den thread Bewerbungen laufen, es verwendet nun das aktualisierte register Werte.
Ich würde schätzen, wenn Sie Verweise hinzufügen, für einige der nicht-offensichtliche details der CLR garbage collector, der Sie hier beschrieben?
InformationsquelleAutor Hans Passant
[ Wollte nur hinzufügen, weiter auf die Interna der Finalization-Prozess ]
So erstellen Sie ein Objekt, und wenn das Objekt gesammelt werden, die das Objekt
Finalize
Methode aufgerufen werden soll. Aber es gibt noch mehr zu beendigen, als diese sehr einfache Annahme.KURZE KONZEPTE::
Objekte, die NICHT an der Umsetzung
Finalize
Methoden, die es der Speicher istzurückgefordert sofort,es sei denn, natürlich, Sie sind nicht reacheable von
der code der Anwendung mehr
Objekte implementieren
Finalize
Methode, Das Konzept/Umsetzungder
Application Roots
,Finalization Queue
,Freacheable Queue
kommtbevor Sie freigegeben werden.
Jedes Objekt wird als Müll, wenn es NICHT reacheable von Anwendung
Code
Übernehmen:: Klassen/Objekte A, B, D, G, H NICHT umsetzen
Finalize
Methode und C, E, F, I, J implementierenFinalize
Methode.Wenn eine Anwendung erstellt ein neues Objekt, den operator new reserviert Speicher auf dem heap. , Wenn den Typ des Objekts enthält ein
Finalize
Methode, dann wird ein Zeiger auf das Objekt gesetzt, auf der finalization queue.daher auch Zeiger auf Objekte C, E, F, I, J Hinzugefügt wird, finalization queue.
Die finalization queue ist eine interne Datenstruktur, gesteuert durch den garbage collector. Jeder Eintrag der Warteschlange verweist auf ein Objekt, dass, sollte seine
Finalize
- Methode aufgerufen, bevor das Objekt Speicher rückgewonnen werden kann.Abbildung unten zeigt einen heap mit mehreren Objekten. Einige dieser Objekte sind erreichbar von der Anwendung Wurzeln, und einige sind nicht. Wenn die Objekte C, E, F, I, und J erstellt wurden, werden die .Net framework erkennt, dass diese Objekte haben
Finalize
Methoden und Zeiger auf diese Objekte Hinzugefügt werden, die finalization queue.Wenn ein GC-Auftritt(1. Sammlung), die Objekte B, E, G, H, I und J sind bestimmt Müll. , Da A, C,D,F sind noch reacheable von Anwendungs-Code, dargestellt durch Pfeile, die aus dem gelben Kasten oben.
Den garbage collector durchsucht die finalization queue suchen für Zeiger auf diese Objekte. , Wenn ein Zeiger gefunden wird, werden die Zeiger entfernt ist von der Fertigstellung der Warteschlange angehängt und der freachable queue ("F-erreichbar").
Die freachable queue ist eine interne Datenstruktur, gesteuert durch den garbage collector. Jeder Zeiger in der freachable queue identifiziert ein Objekt, das bereit ist, seine
Finalize
- Methode aufgerufen.Nach der Abholung(1. Sammlung), die verwaltete heap-sieht etwas ähnlich wie die Abbildung unten. Erklärung unten angegeben: aus:
1.) Der belegte Speicher, der durch Objekte B, G, und H zurückgefordert wurde
sofort, weil diese Gegenstände nicht über eine finalize-Methode,
Bedarf aufgerufen werden.
2.) Jedoch der belegte Speicher, der durch Objekte E, I und J nicht sein könnte
zurückgefordert, weil Ihre
Finalize
Methode wurde nicht genannt aber.Dem Aufruf der Finalize-Methode erfolgt durch freacheable Warteschlange.
3.) A,C,D,F sind noch reacheable von Anwendungs-Code, dargestellt durch
Pfeile aus dem gelben Kasten oben, So dass Sie NICHT erfasst werden, in jedem
Fall
Gibt es eine spezielle runtime-thread gewidmet Aufruf von Finalize-Methoden. Wenn die freachable-queue leer ist (was normalerweise der Fall ist), wird dieser thread schläft. Aber wenn die Einträge angezeigt werden, wird dieser thread wieder aktiviert wird, entfernt jeden Eintrag aus der Warteschlange und ruft jedes Objekt die Finalize-Methode. Der garbage collector komprimiert den ausweisbar-Speicher und dem speziellen runtime-thread leert die freachable queue, ausführen jedes Objekt
Finalize
Methode. So, hier endlich ist, wenn die Finalize-Methode wird ausgeführt,Das nächste mal der garbage collector aufgerufen wird, (2. Sammlung), sieht es, dass die finalisierten Objekte sind wirklich Müll, da die Anwendung die Wurzeln nicht zeigen, es und die freachable queue nicht mehr Punkte auf(es ist LEER), Also der Speicher für die Objekte (E, I, J) sind einfach abgerungen Heap.Siehe Abbildung unten und vergleichen Sie es mit Bild nur oben
Die wichtige Sache zu verstehen ist hier, dass zwei GCs sind erforderlich, um Speicherplatz freizugeben verwendet die Gegenstände, deren Fertigstellung. In der Realität werden mehr als zwei Sammlungen cab noch erforderlich, da diese Objekte bekommen kann gefördert werden, um eine ältere generation
HINWEIS:: Die freachable queue ist als ein root-genauso wie Globale und statische Variablen sind Wurzeln. Daher, wenn ein Objekt auf der freachable queue, dann ist das Objekt erreichbar ist und nicht Müll.
Als Letzte Anmerkung, denken Sie daran, dass die Anwendung Debuggen, ist eine Sache, die Garbage Collection ist eine andere Sache und funktioniert anders. So weit kann man nicht FÜHLEN, garbage collection, nur durch die Anwendungen zu Debuggen, weiter aus, wenn Sie möchten, um zu untersuchen, Speicher hier begann.
InformationsquelleAutor R.C
Gibt es 3 Möglichkeiten, die Sie implementieren können, Speicher-management:-
GC funktioniert nur für verwaltete Ressourcen, daher .NET bieten Dispose und Finalize zur Freigabe von nicht verwalteten Ressourcen wie Strom -, Datenbank-Verbindung, COM-Objekte etc..
1) Entsorgen
Entsorgen muss aufgerufen werden, explizit für die Typen, die IDisposable implementiert.
Programmierer aufrufen muss dieser entweder mit Dispose() oder über konstruieren
Verwenden GC.SuppressFinalize(this) zu verhindern Aufruf Finalizer, wenn Sie schon mit dispose()
2) Abschließen oder Distructor
Heißt es implizit nach Objekt ist berechtigt, für die Bereinigung, finalizer, die für Objekte aufgerufen werden sequenziell durch den finalizer-thread.
Nachteil, dass die Umsetzung der finalizer ist, dass es Speicher zurückfordern wird verzögert, da finalizer für diese Klasse/Typen genannt werden muss vor der Bereinigung, so dass eine zusätzliche colect, um Speicherplatz freizugeben.
3) GC.Collect()
Mittels GC.Collect() nicht unbedingt gesetzt GC für die Sammlung, GC kann immer noch überschreiben, und laufen, Wann immer es will.
auch GC.Collect() wird nur ausgeführt, die Ablaufverfolgung Teil der garbage collection und fügen Sie Artikel zu finalizer-Warteschlange aber nicht nennen Finalizer für die Typen, die gehandhabt wird von einem anderen thread.
Verwenden waitforpendingfinalizers Tauchen, wenn Sie wollen, um sicherzustellen, dass alle Finalizer gewesen callled nach aufrufen von GC.Collect()
InformationsquelleAutor Pankaj Singh