Heim  >  Artikel  >  Backend-Entwicklung  >  Prinzip des .Net-Garbage-Collection-Mechanismus (1)

Prinzip des .Net-Garbage-Collection-Mechanismus (1)

黄舟
黄舟Original
2017-02-17 11:21:291198Durchsuche

Englischer Originaltext: Jeffrey Richter

Zusammengestellt von: Zhao Yukai

Link: http://www.php.cn/

Mit dem Garbage-Collection-Mechanismus in Microsoft.Net clr müssen Programmierer nicht mehr darauf achten, wann Speicher freigegeben werden muss. Die Angelegenheit der Speicherfreigabe wird vollständig vom GC erledigt und ist für Programmierer transparent. Dennoch ist es als .Net-Programmierer notwendig zu verstehen, wie die Garbage Collection funktioniert. In diesem Artikel werfen wir einen Blick darauf, wie .Net verwalteten Speicher zuweist und verwaltet, und beschreiben dann Schritt für Schritt den Algorithmusmechanismus des Garbage Collectors.
Das Entwerfen einer geeigneten Speicherverwaltungsstrategie für ein Programm ist schwierig und mühsam und hindert Sie außerdem daran, sich auf die Lösung des Problems zu konzentrieren, das das Programm selbst zu lösen versucht. Gibt es eine integrierte Methode, die Entwicklern bei der Lösung von Speicherverwaltungsproblemen helfen kann? Natürlich ist es GC in .Net, Garbage Collection.
Lassen Sie uns darüber nachdenken, jedes Programm verbraucht Speicherressourcen: wie Bildschirmanzeige, Netzwerkverbindung, Datenbankressourcen usw. Tatsächlich muss in einer objektorientierten Umgebung jeder Typ einige Speicherressourcen belegen, um seine Daten zu speichern, und zwar gemäß den folgenden Schritten:
1 Leerzeichen
2. Initialisieren Sie den Speicher und versetzen Sie ihn in einen verwendbaren Zustand
3. Greifen Sie auf die Mitglieder des Objekts zu
4 . Zerstören Sie das Objekt und machen Sie den Speicher frei.
Dieses scheinbar einfache Speichernutzungsmuster hat manchmal dazu geführt, dass Programmierer vergessen, Objekte freizugeben die nicht mehr verwendet werden, und versuchen manchmal, auf bereits freigegebene Objekte zuzugreifen. Diese beiden Arten von Fehlern sind normalerweise bis zu einem gewissen Grad verborgen und im Gegensatz zu logischen Fehlern nicht leicht zu finden. Sie können jedoch geändert werden, sobald sie entdeckt wurden. Sie können zu Speicherlecks führen und zu unerwarteten Abstürzen führen, nachdem das Programm eine Zeit lang ausgeführt wurde. Tatsächlich gibt es viele Tools, die Entwicklern dabei helfen können, Speicherprobleme zu erkennen, wie zum Beispiel: Task-Manager, System Überwachen Sie AcitvieX Control und Rational's Purify.
Der GC verlangt von Entwicklern nicht, darauf zu achten, wann Speicher freigegeben wird. Allerdings kann der Garbage Collector nicht alle Ressourcen im Speicher verwalten. Der Garbage Collector weiß nicht, wie er einige Ressourcen recyceln kann. Für diese Ressourcen müssen Entwickler ihren eigenen Code schreiben, um sie zu recyceln. In .Net In Frameworks schreiben Entwickler den Code zum Bereinigen solcher Ressourcen normalerweise in die Methoden Close, Dispose oder Finalize. Diese Methode wird vom Garbage Collector automatisch aufgerufen.
Es gibt jedoch viele Objekte, die den Code nicht selbst implementieren müssen, um Ressourcen freizugeben, wie zum Beispiel: Rechteck. Um es zu löschen, müssen Sie nur die Felder links, rechts, Breite und Höhe löschen Collector kann dies tun. Schauen wir uns an, wie Speicher Objekten zugewiesen wird.
Objektzuordnung:

.Net clr weist alle Referenzobjekte dem verwalteten Heap zu. Dies ist dem C-Runtime-Heap sehr ähnlich, Sie müssen jedoch nicht darauf achten, wann das Objekt freigegeben werden soll. Das Objekt wird automatisch freigegeben, wenn es nicht verwendet wird. Auf diese Weise stellt sich die Frage: Woher weiß der Müllsammler, dass ein Objekt nicht mehr verwendet wird und recycelt werden sollte? Wir werden das später erklären.
Es gibt mehrere Garbage-Collection-Algorithmen. Jeder Algorithmus verfügt über eine Leistungsoptimierung für eine bestimmte Umgebung. In diesem Artikel konzentrieren wir uns auf den Garbage-Collection-Algorithmus von clr. Beginnen wir mit einem Grundkonzept.
Wenn ein Prozess initialisiert wird, wird während der Laufzeit ein kontinuierlicher leerer Speicherplatz reserviert. Dieser Speicherplatz ist der verwaltete Heap. Der verwaltete Heap zeichnet einen Zeiger auf, wir nennen ihn NextObjPtr, der auf die Zuordnungsadresse des nächsten Objekts zeigt. Zunächst zeigt dieser Zeiger auf den Startort des verwalteten Heaps.
Die Anwendung verwendet den neuen Operator, um ein neues Objekt zu erstellen. Dieser Operator muss zunächst bestätigen, dass der verbleibende Speicherplatz des verwalteten Heaps Platz für das Objekt bietet. Zeigen Sie darauf mit dem NextObjPtr-Zeiger Rufen Sie dann den Konstruktor des Objekts auf und der neue Operator gibt die Adresse des Objekts zurück.


Abbildung 1 Verwalteter Heap

Zu diesem Zeitpunkt zeigt NextObjPtr auf den Ort, an dem das nächste Objekt auf dem verwalteten Heap zugewiesen wird. Abbildung 1 zeigt, dass sich in einem verwalteten Heap drei Objekte A, B und C befinden. Das nächste Objekt wird an der Stelle platziert, auf die NextObjPtr zeigt (neben dem C-Objekt)
Schauen wir uns nun noch einmal an, wie der C-Laufzeit-Heap Speicher zuweist. Im C-Laufzeit-Heap muss zum Zuweisen von Speicher die Datenstruktur einer verknüpften Liste durchlaufen werden, bis ein ausreichend großer Speicherblock gefunden wird. Nach der Aufteilung muss der Zeiger in der verknüpften Liste auf den verbleibenden Speicherplatz zeigen . Stellen Sie sicher, dass die verknüpfte Liste intakt ist. Beim verwalteten Heap ändert die Zuweisung eines Objekts nur den Zeiger des NextObjPtr-Zeigers, was sehr schnell ist. Tatsächlich kommt die Zuweisung eines Objekts auf dem verwalteten Heap der Zuweisung von Speicher auf dem Thread-Stack sehr nahe.
Bisher scheint die Geschwindigkeit der Speicherzuweisung auf dem verwalteten Heap schneller zu sein als auf dem C-Laufzeit-Heap, und die Implementierung ist auch einfacher. Natürlich hat der verwaltete Heap diesen Vorteil, weil er davon ausgeht, dass der Adressraum unbegrenzt ist. Offensichtlich ist diese Annahme falsch. Es muss einen Mechanismus geben, der sicherstellt, dass diese Annahme wahr ist. Dieser Mechanismus ist der Garbage Collector. Mal sehen, wie es funktioniert.
Wenn die Anwendung den neuen Operator aufruft, um ein Objekt zu erstellen, ist möglicherweise kein Speicher zum Speichern des Objekts vorhanden. Der verwaltete Heap kann erkennen, ob der von NextObjPtr angezeigte Speicherplatz die Größe des Heaps überschreitet. Wenn er die Größe des Heaps überschreitet, bedeutet dies, dass der verwaltete Heap voll ist und eine Speicherbereinigung erforderlich ist.
In Wirklichkeit wird eine Speicherbereinigung ausgelöst, nachdem der Heap der Generation 0 voll ist. „Generierung“ ist ein Implementierungsmechanismus für den Garbage Collector zur Verbesserung der Leistung. „Generation“ bedeutet: Neu erstellte Objekte sind die junge Generation, und Objekte, die vor dem Recyclingvorgang nicht recycelt werden, sind ältere Objekte. Durch die Aufteilung von Objekten in Generationen kann der Garbage Collector nur Objekte einer bestimmten Generation sammeln, anstatt alle Objekte zu sammeln.

Garbage Collection-Algorithmus:

Der Garbage Collector prüft, ob es Objekte gibt, die von der Anwendung nicht mehr verwendet werden. Wenn solche Objekte vorhanden sind, kann der von diesen Objekten belegte Speicherplatz zurückgefordert werden (wenn auf dem Heap nicht genügend Speicher verfügbar ist, löst der neue Operator eine OutofMemoryException aus). Sie fragen sich vielleicht, wie der Garbage Collector feststellt, ob ein Objekt noch verwendet wird? Diese Frage ist nicht einfach zu beantworten.
Jede Anwendung verfügt über eine Reihe von Stammobjekten. Sie können auf eine Adresse im verwalteten Heap verweisen oder null sein. Beispielsweise sind alle globalen und statischen Objektzeiger Stammobjekte der Anwendung. Darüber hinaus sind lokale Variablen/Parameter auf dem Thread-Stack ebenfalls Stammobjekte der Anwendung, und Objekte in den CPU-Registern, die auf den verwalteten Heap verweisen, sind ebenfalls Stammobjekte . Die Liste der verbleibenden Root-Objekte wird vom JIT-Compiler (Just-in-Time) und CLR verwaltet, und der Garbage Collector kann auf diese Root-Objekte zugreifen.
Wenn der Garbage Collector gestartet wird, geht er davon aus, dass alle Objekte auf dem verwalteten Heap Müll sind. Das heißt, es wird davon ausgegangen, dass es kein Root-Objekt und keine Objekte gibt, auf die das Root-Objekt verweist. Der Garbage Collector beginnt dann mit der Durchquerung des Stammobjekts und erstellt ein Diagramm aller Objekte, die einen Verweis auf das Stammobjekt haben.
Abbildung 2 zeigt, dass die Stammobjekte der Anwendung auf dem verwalteten Heap A, C, D und F sind. Diese Objekte sind Teil des Diagramms. Dann verweist Objekt D auf Objekt H, sodass auch Objekt H hinzugefügt wird graph. ;Der Garbage Collector durchläuft alle erreichbaren Objekte.


Bild 2 Objekte auf dem verwalteten Heap

Der Garbage Collector durchläuft das Stammobjekt und verweist nacheinander auf Objekte. Wenn der Garbage Collector feststellt, dass sich ein Objekt bereits im Diagramm befindet, ändert er den Pfad und durchläuft ihn weiter. Dies dient zwei Zwecken: Zum einen soll die Leistung verbessert werden, zum anderen sollen Endlosschleifen vermieden werden.
Nachdem alle Root-Objekte überprüft wurden, enthält das Diagramm des Garbage Collectors alle erreichbaren Objekte in der Anwendung. Alle Objekte auf dem verwalteten Heap, die nicht in diesem Diagramm enthalten sind, sind Müllobjekte, die recycelt werden müssen. Nach der Erstellung des erreichbaren Objektdiagramms beginnt der Garbage Collector, den verwalteten Heap linear zu durchqueren, um Blöcke aufeinanderfolgender Garbage-Objekte zu finden (die als freier Speicher betrachtet werden können). Der Garbage Collector verschiebt dann die Nicht-Müllobjekte zusammen (mithilfe der memcpy-Funktion in C) und deckt dabei alle Speicherfragmente ab. Deaktivieren Sie natürlich alle Objektzeiger, wenn Sie Objekte verschieben (da diese möglicherweise falsch sind). Daher muss der Garbage Collector die Stammobjekte der Anwendung so ändern, dass sie auf die neue Speicheradresse des Objekts verweisen. Wenn ein Objekt außerdem einen Zeiger auf ein anderes Objekt enthält, ist der Garbage Collector auch für die Änderung der Referenz verantwortlich. Abbildung 3 zeigt den verwalteten Heap nach einer Sammlung.


Bild 3 Der verwaltete Heap nach dem Recycling
ist in Abbildung 3 dargestellt. Nach dem Recycling werden alle Müllobjekte identifiziert und alle Nicht-Müllobjekte werden zusammen verschoben. Die Zeiger aller Nicht-Müllobjekte werden ebenfalls auf die verschobenen Speicheradressen geändert, und NextObjPtr zeigt auf die Rückseite des letzten Nicht-Müllobjekts. Zu diesem Zeitpunkt kann der neue Operator weiterhin erfolgreich Objekte erstellen.
Wie Sie sehen, gibt es bei der Garbage Collection einen erheblichen Leistungsverlust, was ein klarer Nachteil der Verwendung des verwalteten Heaps ist. Bedenken Sie jedoch, dass der Speicherrückgewinnungsvorgang erst dann ausgeführt wird, wenn der verwaltete Heap langsam ist. Die Leistung des verwalteten Heaps ist besser als die Leistung des C-Laufzeit-Heaps, bis dieser voll ist. Der Laufzeit-Garbage Collector führt auch einige Leistungsoptimierungen durch, über die wir im nächsten Artikel sprechen werden.
Der folgende Code veranschaulicht, wie Objekte erstellt und verwaltet werden:

class Application {
public static int Main(String[] args) {
 
      // ArrayList object created in heap, myArray is now a root
      ArrayList myArray = new ArrayList();
 
      // Create 10000 objects in the heap
      for (int x = 0; x < 10000; x++) {
         myArray.Add(new Object());    // Object object created in heap
      }
 
      // Right now, myArray is a root (on the thread&#39;s stack). So, 
      // myArray is reachable and the 10000 objects it points to are also 
      // reachable.
      Console.WriteLine(a.Length);
 
      // After the last reference to myArray in the code, myArray is not 
      // a root.
      // Note that the method doesn&#39;t have to return, the JIT compiler 
      // knows
      // to make myArray not a root after the last reference to it in the 
      // code.
 
      // Since myArray is not a root, all 10001 objects are not reachable
      // and are considered garbage.  However, the objects are not 
      // collected until a GC is performed.
   }
}

Vielleicht fragen Sie sich: GC ist so gut, warum ist es nicht in ANSI C++ enthalten? Der Grund dafür ist, dass der Garbage Collector in der Lage sein muss, die Stammobjektliste der Anwendung und den Zeiger des Objekts zu finden. In C++ können Objektzeiger ineinander konvertiert werden, und es gibt keine Möglichkeit zu wissen, auf welches Objekt der Zeiger zeigt. In der CLR kennt der verwaltete Heap den tatsächlichen Typ des Objekts. Die Metadateninformationen können verwendet werden, um zu bestimmen, auf welche Mitgliedsobjekte sich das Objekt bezieht.

Garbage Collection und Finalisierung

Der Garbage Collector bietet eine zusätzliche Funktion, die automatisch die Finalize-Methode eines Objekts aufrufen kann, nachdem es als Garbage markiert wurde (vorausgesetzt, dass die object Überschreibt die Finalize-Methode des Objekts).
Die Finalize-Methode ist eine virtuelle Methode des Objektobjekts. Sie können diese Methode bei Bedarf überschreiben, diese Methode kann jedoch nur auf ähnliche Weise wie der C++-Destruktor überschrieben werden. Zum Beispiel:

{
~Foo(){
        Console.WriteLine(“Foo Finalize”);
}
}

Programmierer, die hier C++ verwendet haben, sollten besonders darauf achten, dass die Finalize-Methode genauso geschrieben ist wie der Destruktor in C++. Allerdings sind die Finalize-Methode und der Destruktor in . Anders ausgedrückt: Verwaltete Objekte können nicht zerstört und nur durch Garbage Collection recycelt werden.
Wenn Sie eine Klasse entwerfen, ist es aus folgenden Gründen am besten, das Überschreiben der Finalize-Methode zu vermeiden:
1. Objekte, die Finalize implementieren, werden auf eine ältere „Generation“ heraufgestuft, was den Speicherdruck erhöht und die Objekt und Die zugehörigen Objekte dieses Objekts können nicht recycelt werden, wenn sie zum ersten Mal zu Müll werden.
2. Die Zuordnungszeit dieser Objekte wird länger sein.
3 Wenn Sie den Garbage Collector die Finalize-Methode ausführen lassen, wird die Leistung erheblich beeinträchtigt. Bitte denken Sie daran, dass jedes Objekt, das die Finalize-Methode implementiert, die Finalize-Methode ausführen muss. Wenn es ein Array-Objekt mit einer Länge von 10000 gibt, muss jedes Objekt die Finalize-Methode ausführen
4 Andere Objekte, die die Finalize-Methode nicht implementieren, werden ebenfalls für das Recycling verzögert
5 Sie haben keine Möglichkeit zu steuern, wann die Finalize-Methode ausgeführt wird. Wenn Sie Ressourcen wie Datenbankverbindungen in der Finalize-Methode freigeben möchten, kann dies dazu führen, dass die Datenbankressourcen lange nach dem Zeitpunkt
6 freigegeben werden. Wenn das Programm abstürzt, werden einige Objekte immer noch referenziert, ihre Finalize-Methoden jedoch nicht Gelegenheit umgesetzt. Diese Situation tritt auf, wenn das Objekt in einem Hintergrundthread verwendet wird, wenn das Objekt das Programm verlässt oder wenn die AppDomain entladen wird. Darüber hinaus wird die Finalize-Methode standardmäßig nicht ausgeführt, wenn die Anwendung beendet werden muss. Natürlich werden alle Betriebssystemressourcen zurückgefordert; Objekte auf dem verwalteten Heap werden jedoch nicht zurückgefordert. Sie können dieses Verhalten ändern, indem Sie die RequestFinalizeOnShutdown-Methode des GC aufrufen.
7. Die Laufzeit kann die Reihenfolge, in der Finalize-Methoden mehrerer Objekte ausgeführt werden, nicht steuern. Manchmal kann die Zerstörung von Objekten sequentiell erfolgen.
Wenn das von Ihnen definierte Objekt die Finalize-Methode implementieren muss, stellen Sie sicher, dass die Finalize-Methode so schnell wie möglich ausgeführt wird, und vermeiden Sie alle Vorgänge, die zu Blockierungen führen können, einschließlich aller Thread-Synchronisierungsvorgänge. Stellen Sie außerdem sicher, dass die Finalize-Methode keine Ausnahme verursacht. Wenn eine Ausnahme auftritt, führt der Garbage Collector weiterhin die Finalize-Methode anderer Objekte aus und ignoriert die Ausnahme.
Wenn der Compiler Code generiert, ruft er automatisch den Konstruktor der Basisklasse im Konstruktor auf. In ähnlicher Weise fügt der C++-Compiler automatisch einen Aufruf zum Destruktor der Basisklasse für den Destruktor hinzu. Die Finalize-Funktion in .Net ist jedoch nicht so und der Compiler führt keine spezielle Verarbeitung für die Finalize-Methode durch. Wenn Sie die Finalize-Methode der übergeordneten Klasse in der Finalize-Methode aufrufen möchten, müssen Sie den aufrufenden Code explizit selbst hinzufügen.
Bitte beachten Sie, dass die Finalize-Methode in C# genauso geschrieben ist wie der Destruktor in C++, C# jedoch keine Destruktoren unterstützt. Lassen Sie sich von dieser Schreibweise nicht täuschen.

Die interne Implementierung des GC, die die Finalize-Methode aufruft

Oberflächlich betrachtet ist es für den Garbage Collector sehr einfach, die Finalize-Methode zu verwenden. Sie erstellen ein Objekt und rufen es auf Finalize-Methode, wenn das Objekt recycelt wird. Aber es ist tatsächlich etwas komplizierter.
Wenn eine Anwendung ein neues Objekt erstellt, weist der neue Operator Speicher auf dem Heap zu. Wenn das Objekt die Finalize-Methode implementiert. Der Objektzeiger wird in die Finalisierungswarteschlange gestellt. Die Finalisierungswarteschlange ist eine interne Datenstruktur, die vom Garbage Collector gesteuert wird. Jedes Objekt in der Warteschlange muss beim Recycling seine Finalize-Methode aufrufen.
Die folgende Abbildung zeigt, dass der Heap mehrere Objekte enthält, von denen einige Objekte sind und andere nicht. Wenn die Objekte C, E, F, I und J erstellt werden, erkennt das System, dass diese Objekte die Finalize-Methode implementieren und stellt ihre Zeiger in die Finalisierungswarteschlange.


Was die Finalize-Methode bewirkt, ist normalerweise Recycling, das von der nicht recycelt werden kann Garbage Collector-Ressourcen wie Dateihandles, Datenbankverbindungen usw.
Bei der Garbage Collection werden die Objekte B, E, G, H, I und J als Garbage markiert. Der Garbage Collector durchsucht die Finalisierungswarteschlange, um Zeiger auf diese Objekte zu finden. Wenn ein Objektzeiger gefunden wird, wird der Zeiger in die Freachable-Warteschlange verschoben. Die Freachable-Warteschlange ist eine weitere interne Datenstruktur, die vom Garbage Collector gesteuert wird. Die Finalize-Methode jedes Objekts in der Freachable-Warteschlange wird ausgeführt.
Nach der Garbage Collection ist der verwaltete Heap wie in Abbildung 6 dargestellt. Sie können sehen, dass die Objekte B, G und H recycelt wurden, da diese Objekte keine Finalize-Methode haben. Die Objekte E, I und J wurden jedoch noch nicht recycelt, da ihre Finalize-Methoden noch nicht ausgeführt wurden.


Bild 5 Verwalteter Heap nach Garbage Collection

Wenn das Programm ausgeführt wird, gibt es einen dedizierten Thread, der für den Aufruf der Finalize-Methode des Objekts im verantwortlich ist Zufällige Warteschlange. Wenn die Freachable-Warteschlange leer ist, wird dieser Thread in den Ruhezustand versetzt. Wenn sich Objekte in der Warteschlange befinden, wird der Thread aktiviert, entfernt die Objekte in der Warteschlange und ruft ihre Finalize-Methode auf. Versuchen Sie daher nicht, beim Ausführen der Finalize-Methode auf die lokale Thread-Umgebung zuzugreifen. Lagerung.
Das Zusammenspiel zwischen der Finalisierungswarteschlange und der Freachable-Warteschlange ist sehr clever. Lassen Sie mich zunächst erklären, wie freachable zu seinem Namen kam. F ist offensichtlich Finalisierung; jedes Objekt in dieser Warteschlange wartet darauf, seine Finalize-Methode auszuführen; Mit anderen Worten: Objekte in einer Freachable-Warteschlange werden als verwandte Objekte betrachtet, genau wie globale Variablen oder statische Variablen. Wenn sich also ein Objekt in der Freachable-Warteschlange befindet, ist das Objekt kein Müll.
Einfach ausgedrückt: Wenn ein Objekt nicht erreichbar ist, betrachtet der Garbage Collector das Objekt als Müll. Wenn der Garbage Collector dann Objekte aus der Finalisierungswarteschlange in die Freachable-Warteschlange verschiebt, sind diese Objekte kein Müll mehr und ihr Speicher wird nicht zurückgefordert. Zu diesem Zeitpunkt hat der Garbage Collector die Markierung von Garbage abgeschlossen und einige als Garbage markierte Objekte wurden erneut als Nicht-Müllobjekte betrachtet. Der Garbage Collector fordert komprimierten Speicher zurück, löscht die freachable-Warteschlange und führt die Finalize-Methode für jedes Objekt in der Warteschlange aus.


Abbildung 6 Verwalteter Heap nach erneuter Durchführung der Garbage Collection

再次出发垃圾回收之后,实现Finalize方法的对象才被真正的回收。这些对象的Finalize方法已经执行过了,Freachable队列清空了。

垃圾回收让对象复活

在前面部分我们已经说了,当程序不使用某个对象时,这个对象会被回收。然而,如果对象实现了Finalize方法,只有当对象的Finalize方法执行之后才会认为这个对象是可回收对象并真正回收其内存。换句话说,这类对象会先被标识为垃圾,然后放到freachable队列中复活,然后执行Finalize之后才被回收。正是Finalize方法的调用,让这种对象有机会复活,我们可以在Finalize方法中让某个对象强引用这个对象;那么垃圾回收器就认为这个对象不再是垃圾了,对象就复活了。
如下复活演示代码:

public class Foo {
~Foo(){
Application.ObjHolder = this;
  }
}
 
class Application{
  static public Object ObjHolder = null;
}

在这种情况下,当对象的Finalize方法执行之后,对象被Application的静态字段ObjHolder强引用,成为根对象。这个对象就复活了,而这个对象引用的对象也就复活了,但是这些对象的Finalize方法可能已经执行过了,可能会有意想不到的错误发生。
事实上,当你设计自己的类型时,对象的终结和复活有可能完全不可控制。这不是一个好现象;处理这种情况的常用做法是在类中定义一个bool变量来表示对象是否执行过了Finalize方法,如果执行过Finalize方法,再执行其他方法时就抛出异常。
现在,如果有其他的代码片段又将Application.ObjHolder设置为null,这个对象变成不可达对象。最终垃圾回收器会把对象当成垃圾并回收对象内存。请注意这一次对象不会出现在finalization队列中,它的Finalize方法也不会再执行了。
复活只有有限的几种用处,你应该尽可能避免使用复活。尽管如此,当使用复活时,最好重新将对象添加到终结队列中,GC提供了静态方法ReRegisterForFinalize方法做这件事:

如下代码:

public class Foo{
~Foo(){
Application.ObjHolder = this;
GC.ReRegisterForFinalize(this);
}
}

当对象复活时,重新将对象添加到复活队列中。需要注意的时如果一个对象已经在终结队列中,然后又调用了GC.ReRegisterForFinalize(obj)方法会导致此对象的Finalize方法重复执行。
垃圾回收机制的目的是为开发人员简化内存管理。
下一篇我们谈一下弱引用的作用,垃圾回收中的“代”,多线程中的垃圾回收和与垃圾回收相关的性能计数器。

 以上就是.Net 垃圾回收机制原理(一)的内容,更多相关内容请关注PHP中文网(www.php.cn)! 


Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn