Original: http://www.mikel.cn/article.asp?id=1698
Kennen Sie den Garbage-Collection-Mechanismus von .Net? Können Sie kurz beschreiben, wie GC funktioniert? Wie können wir das Gedächtnis effektiv verwalten? Welche Rolle spielt das im Hauptteil der Using-Anweisung instanziierte Objekt?
Dieser Abschnitt ist wie folgt gegliedert: 1. Netztypen und Speicherzuweisung 2. Funktionsweise des GC-Garbage Collectors 3. Was sind nicht verwaltete Ressourcen 4. Wie werden Objektressourcen effektiv freigegeben? Zusammenfassung: Beginnen wir jetzt mit dem Studium dieses Abschnitts.
1..Netztypen und Speicherzuordnung
Alle Typen in Net werden (direkt oder indirekt) vom System.Object-Typ abgeleitet.
Typen in CTS sind in zwei Kategorien unterteilt: Referenztypen (Referenztypen, auch verwaltete Typen genannt), die im Speicherheap zugeordnet sind, und Werttypen. Werttypen werden auf dem Stapel zugewiesen. Wie im Bild gezeigt
Werttypen befinden sich auf dem Stapel, zuerst in, zuletzt heraus. Werttypvariablen haben eine Lebensreihenfolge. Dadurch wird sichergestellt, dass Werttypvariablen Ressourcen freigeben, bevor sie aus dem Gültigkeitsbereich verschoben werden. Einfacher und effizienter als Referenztypen. Der Stapel weist Speicher von hohen Adressen zu niedrigen Adressen zu.
Der Referenztyp wird auf dem verwalteten Heap (Managed Heap) zugewiesen und eine Variable wird auf dem Stapel deklariert und gespeichert. Wenn new zum Erstellen eines Objekts verwendet wird, wird die Adresse des Objekts in dieser Variablen gespeichert. Im Gegensatz dazu weist der verwaltete Heap Speicher von niedrigen Adressen zu hohen Adressen zu, wie in der Abbildung dargestellt
2. Funktionsweise des GC-Garbage Collectors
Wenn in der obigen Abbildung das DataSet abläuft, wird die Zerstörung des Objekts nicht angezeigt, und die Objekte auf dem Heap bleiben bestehen und warten auf das GC-Recycling.
Es wird empfohlen, ist jedoch nicht erforderlich, dass der Garbage Collector die Objektalterung durch Generierung unterstützt. Eine Generation ist eine Einheit von Objekten mit relativem Alter im Speicher. Objekt
Der Codename oder das Alter identifiziert die Generation, zu der das Objekt gehört. Im Lebenszyklus der Anwendung gehören Objekte, die in jüngerer Zeit erstellt wurden, zu einer neueren Generation und haben einen höheren Wert als Objekte, die früher erstellt wurden.
Untere Subcodenummer. Der Objektcode in der neuesten Generation ist 0.
Wenn Sie ein neues Objekt erstellen, müssen Sie zunächst die freie verknüpfte Liste durchsuchen, um den am besten geeigneten Speicherblock zu finden, ihn zuordnen, die verknüpfte Liste des Speicherblocks anpassen und Fragmente zusammenführen. Der neue Vorgang kann in fast O(1)-Zeit abgeschlossen werden, indem 1 zum oberen Zeiger des Heaps hinzugefügt wird. Das Funktionsprinzip lautet: Wenn der verbleibende Speicherplatz auf dem verwalteten Heap nicht ausreicht oder der Speicherplatz von Generator 0 voll ist, wird der GC ausgeführt und beginnt, Speicher zurückzugewinnen. Zu Beginn der Garbage Collection komprimiert und passt der GC den Heap-Speicher an, und die Objekte werden oben konzentriert. GC beansprucht beim Scannen von Müll eine gewisse CPU-Zeit. Der ursprüngliche GC-Algorithmus scannt tatsächlich den gesamten Heap, was ineffizient ist. Der aktuelle GC unterteilt die Objekte im Heap in drei Generationen. Der jüngste Eintrag im Heap ist Generation 0, gefolgt von Generation 1 und Generation 2. Der erste GC scannt nur Generation 0. Wenn der zurückgewonnene Speicherplatz für die aktuelle Nutzung ausreicht, besteht keine Notwendigkeit, Objekte in anderen Generationen zu scannen. Daher erstellt GC Objekte effizienter als C++ und muss nicht den gesamten Heap-Speicherplatz scannen. Die durch die Scan-Strategie und die Speicherverwaltungsstrategie erzielte Leistungsverbesserung reicht aus, um die vom GC belegte CPU-Zeit auszugleichen.
3. Was sind nicht verwaltete Ressourcen?
Eine übliche nicht verwaltete Ressource ist ein Objekt, das eine Betriebssystemressource wie eine Datei, ein Fenster oder eine Netzwerkverbindung umschließt. Der Garbage Collector kann bei solchen Ressourcen zwar die Lebensdauer des Objekts verfolgen, das die nicht verwaltete Ressource umschließt, weiß aber, wie das geht Bereinigen Sie diese Ressourcen. Glücklicherweise ermöglicht die vom .net Framework bereitgestellte Finalize()-Methode die ordnungsgemäße Bereinigung nicht verwalteter Ressourcen, bevor der Garbage Collector diese Ressourcen recycelt. Hier sind mehrere gängige nicht verwaltete Ressourcen: Pinsel, Stream-Objekte, Komponentenobjekte und andere Ressourcen (Object, OdbcDataReader, OleDBDataReader, Pen, Regex, Socket, StreamWriter, ApplicationContext, Brush,
Component,ComponentDesigner,Container,Context,Cursor,FileStream,
Schriftart, Symbol, Bild, Matrix, Timer, Tooltip). (Siehe MSDN)
4. So geben Sie nicht verwaltete Ressourcen effektiv frei.
GC kann nicht verwaltete Ressourcen nicht verwalten. Wie kann man also nicht verwaltete Ressourcen freigeben? .Net bietet zwei Methoden:
(1) Destruktor: Wenn der Garbage Collector die Ressourcen eines nicht verwalteten Objekts recycelt, ruft er die Finalisierungsmethode des Objekts Finalize() auf, um die Ressourcen zu bereinigen. Aufgrund der Einschränkungen der GC-Arbeitsregeln ruft der GC jedoch Finalize des Objekts auf Methode Die Ressource wird nicht einmal freigegeben und das Objekt wird nach dem zweiten Aufruf gelöscht.
(2) Erben Sie die IDisposable-Schnittstelle und implementieren Sie die Dispose()-Methode. Die IDisposable-Schnittstelle definiert ein Muster (mit Unterstützung auf Sprachebene), bietet einen bestimmten Mechanismus zum Freigeben nicht verwalteter Ressourcen und vermeidet die inhärenten Probleme mit der Garbage Collection von Destruktoren -bezogene Probleme.
Um den Garbage-Collection-Mechanismus besser zu verstehen, habe ich einen Teil des Codes speziell geschrieben und detaillierte Kommentare hinzugefügt. Definieren Sie eine einzelne Klasse: FrankClassWithDispose (erbt die Schnittstelle IDisposable), FrankClassNoFinalize (kein Finalizer), FrankClassWithDestructor (definiert den Destruktor).
Der spezifische Code lautet wie folgt:
Code
1 mit System;
2 mit System.Collections.Generic;
3 mit System.Text;
4 mit System.Data;
5 mit System.Data.Odbc;
6 mit System.Drawing;
7 // Codiert von Frank Xu Lei 18.2.2009
8 // Studieren Sie die .NET-Speicherverwaltung
9 // Garbage Collector Garbage Collector. Gehostete Ressourcen können bei Bedarf gemäß den Richtlinien zurückgefordert werden.
10 // Aber der GC weiß nicht, wie er nicht verwaltete Ressourcen verwalten soll. Wie Netzwerkverbindung, Datenbankverbindung, Pinsel, Komponente usw.
11 //Zwei Mechanismen zur Lösung des Problems der Freigabe nicht verwalteter Ressourcen. Destruktor, IDispose-Schnittstelle
12 // COM-Referenzanzahl
13 // C++-Handbuchverwaltung, Neues Löschen
14 // VB automatische Verwaltung
15 Namespace MemoryManagement
16 {
17 // Erben Sie die Schnittstelle IDisposable, implementieren Sie die Dispose-Methode und geben Sie die Instanzressourcen von FrankClassDispose frei
18 öffentliche Klasse FrankClassWithDispose: IDisposable
19 {
20 private OdbcConnection _odbcConnection = null;
einundzwanzig
22 // Konstrukteur
23 public FrankClassWithDispose()
vierundzwanzig {
25 if (_odbcConnection == null )
26 _odbcConnection = new OdbcConnection();
27 Console.WriteLine( "FrankClassWithDispose wurde erstellt");
28 }
29 //Testmethode
30 public void DoSomething()
31 {
32
33 /**/ /// /code hier, um etwas zu tun
34 Rückkehr;
35}
36 // Implementieren Entsorgen und geben Sie die von dieser Klasse verwendeten Ressourcen frei
37 public void Dispose()
38 {
39 if (_odbcConnection != null )
40 _odbcConnection.Dispose();
41 Console.WriteLine( "FrankClassWithDispose wurde entsorgt");
42 }
43}
44 // Finalize ist nicht implementiert. Warten Sie, bis GC die Instanzressourcen von FrankClassFinalize wiederverwendet, und recyceln Sie sie direkt, wenn GC ausgeführt wird.
45 öffentliche Klasse FrankClassNoFinalize
46 {
47 private OdbcConnection _odbcConnection = null ;
48 //Konstrukteur
49 public FrankClassNoFinalize()
50 {
51 if (_odbcConnection == null )
52 _odbcConnection = new OdbcConnection();
53 Console.WriteLine( " FrankClassNoFinalize wurde erstellt " );
54 }
55 //Testmethode
56 public void DoSomething()
57 {
58
59 // GC.Collect();
60 /**/ /// /code hier, um etwas zu tun
61 Rückkehr;
62 }
63}
64 // Implementieren Sie den Destruktor, kompilieren Sie ihn in die Finalize-Methode und rufen Sie den Destruktor des Objekts auf
65 // Bei laufendem GC wird die Ressource nicht beim ersten Aufruf freigegeben, sondern erst beim zweiten Aufruf.
66 //Instanzressourcen von FrankClassDestructor
67 // Die CLR verwendet einen unabhängigen Thread, um die Finalize-Methode des Objekts auszuführen.
68 öffentliche Klasse FrankClassWithDestructor
69 {
70 private OdbcConnection _odbcConnection = null ;
71 //Konstrukteur
72 public FrankClassWithDestructor()
73 {
74 if (_odbcConnection == null )
75 _odbcConnection = new OdbcConnection();
76 Console.WriteLine( " FrankClassWithDestructor wurde erstellt " );
77 }
78 //Testmethode
79 public void DoSomething()
80 {
81 /**/ /// /code hier, um etwas zu tun
82
83 Rückkehr;
84}
85 // Destruktor, gibt nicht verwaltete Ressourcen frei
86 ~ FrankClassWithDestructor()
87 {
88 if (_odbcConnection != null )
89 _odbcConnection.Dispose();
90 Console.WriteLine( " FrankClassWithDestructor wurde entsorgt " );
91 }
92 }
93}
94
Es wird eine Instanz des nicht verwalteten Objekts OdbcConnection verwendet. Der gebaute Client wurde kurz getestet. Der Client-Code lautet wie folgt:
Code
1 mit System;
2 mit System.Collections.Generic;
3 mit System.Text;
4 mit System.Data;
5 mit MemoryManagement;
6 // Codiert von Frank Xu Lei 18.2.2009
7 // Studieren Sie die .NET-Speicherverwaltung
8 // Testen Sie die zurückgeforderten nicht verwalteten Objekte.
9 // Tests für nicht verwalteten Code, Vergleich
10 // Bei verwaltetem Code kann GC ihn strategischer selbst recyceln oder IDisposable implementieren, die Dispose()-Methode aufrufen und ihn aktiv freigeben.
11 Namespace MemoryManagementClient
12 {
13 Unterrichtsprogramme
14 {
15 static void Main( string [] args)
16 {
17
18 /**/ ////////////////////////////////////// //(1 ) / ///////////////////////////////////////// //
19 // Rufen Sie die Dispose()-Methode auf, um sie aktiv freizugeben. Ressourcen, Flexibilität
20 FrankClassWithDispose _frankClassWithDispose = null ;
21 Versuche
zweiundzwanzig {
23 _frankClassWithDispose = new FrankClassWithDispose();
24 _frankClassWithDispose.DoSomething();
25
26}
27 endlich
28 {
29 if (_frankClassWithDispose != null )
30 _frankClassWithDispose.Dispose();
31 // Console.WriteLine("FrankClassWithDispose-Instanz wurde freigegeben");
32}
33
34 /**/ ////////////////////////////////////// //(2 ) / //////////////////////////////////////////// /
35 // Mit der Using-Anweisung können Sie ein nicht verwaltetes Objekt erstellen, bevor die Methodenausführung endet
36 mit (FrankClassWithDispose _frankClassWithDispose2 = new FrankClassWithDispose())
37 {
38 // _frankClassWithDispose2.DoSomething();
39 }
40
41 /**/ ////////////////////////////////////// //(3 ) / ///////////////////////////////////////// //
42 //Wenn der Garbage Collector läuft, werden Ressourcen einmalig freigegeben
43 FrankClassNoFinalize _frankClassNoFinalize = new FrankClassNoFinalize();
44 _frankClassNoFinalize.DoSomething();
45
46 /**/ ////////////////////////////////////////// (4) ///////////////////////////////////////////////////////////////////////////////// / /
47 // Wenn der Garbage Collector ausgeführt wird, dauert es zweimal, bis Ressourcen freigegeben werden.
48 FrankClassWithDestructor _frankClassWithDestructor = new FrankClassWithDestructor();
49 _frankClassWithDestructor.DoSomething();
50 /**/ ///////////////////////////////////////// / (5 ) ////////////////////////////////////////////////////////////////////////// /
51 // Die Using-Anweisung kann nicht zum Erstellen eines Objekts verwendet werden, da sie die IDispose-Schnittstelle nicht implementiert.
52 // using (FrankClassWithDestructor _frankClassWithDestructor2 = new FrankClassWithDestructor())
53 // {
54 // _frankClassWithDestructor2.DoSomething();
55 // }
56
57 /**/ ////////////////////////////////////////// /// /////////////////////////////////////// //
58 // Zum Debuggen
59 Console.WriteLine( "Drücken Sie eine beliebige Taste, um fortzufahren");
60 Console.ReadLine();
61
62
63}
64}
65 }
66
Manchmal müssen Ressourcen zu einem bestimmten Zeitpunkt freigegeben werden. Eine Klasse kann die Schnittstelle IDisposable implementieren, die die Ressourcenverwaltungs- und Bereinigungsaufgabenmethoden IDisposable.Dispose ausführt.
Wenn der Aufrufer die Dispose-Methode aufrufen muss, um das Objekt zu bereinigen, muss die Klasse die Dispose-Methode als Teil des Vertrags implementieren. Der Garbage Collector ruft standardmäßig nicht auf
Die Dispose-Methode kann jedoch durch die Implementierung der Dispose-Methode Methoden im GC aufrufen, um das endgültige Verhalten des Garbage Collectors zu regulieren.
Es ist erwähnenswert, dass der Aufruf der Dispose()-Methode aktiv Ressourcen freigibt und flexibel ist. Sie können die Using-Anweisung verwenden, um nicht verwaltete Objekte zu erstellen, bevor die Methodenausführung endet
Die Dispose()-Methode gibt Ressourcen frei. Die Wirkung der beiden Enden des Codes ist die gleiche. Sie können die kompilierte IL anzeigen.
Code
1. Versuchen
2 {
3 IL_0003: nein
4 IL_0004: newobj-Instanz void [MemoryManagement]MemoryManagement.FrankClassWithDispose::.ctor()
5 IL_0009: stloc
6 IL_000a: ldloc 0
7 IL_000b: callvirt-Instanz void [MemoryManagement]MemoryManagement.FrankClassWithDispose::DoSomething()
8 IL_0010: nein
9 IL_0011: nein
10 IL_0012: Leave.s IL_0028
11 } // end .try
12 endlich
13 {
14 IL_0014: nein
15 IL_0015: ldloc 0
16 IL_0016: ldnull
17 IL_0017: ceq
18 IL_0019: stloc.s CS$ 4 $ 0000
19 IL_001b: ldloc.s CS$ 4 $ 0000
20 IL_001d: brtrue.s IL_0026
21 IL_001f: ldloc 0
22 IL_0020: callvirt-Instanz ungültig [MemoryManagement]MemoryManagement.FrankClassWithDispose::Dispose()
23 IL_0025: nein
24 IL_0026: nein
25 IL_0027: endfinally
26 } // Handler beenden
27 IL_0028: nein
28 IL_0029: newobj-Instanz ungültig [MemoryManagement]MemoryManagement.FrankClassWithDispose::.ctor()
29 IL_002e: stloc
30. Versuchen
31 {
32 IL_002f: nein
33 IL_0030: nein
34 IL_0031: Leave.s IL_0045
35 } // end .try
36 endlich
37 {
38 IL_0033: ldloc 1
39 IL_0034: ldnull
40 IL_0035: ceq
41 IL_0037: stloc.s CS$ 4 $ 0000
42 IL_0039: ldloc.s CS$ 4 $ 0000
43 IL_003b: brtrue.s IL_0044
44 IL_003d: ldloc 1
45 IL_003e: Callvirt-Instanz ungültig [mscorlib]System.IDisposable::Dispose()
46 IL_0043: nein
47 IL_0044: endfinally
48 } // Handler beenden
49
Die Using-Anweisung hat den gleichen Effekt, wenn es darum geht, nicht verwaltete Objektressourcen freizugeben. Dies kommt häufig in Vorstellungsgesprächen vor, z. B. nach der Verwendung des Schlüsselworts „Using“ und ähnlichen Fragen. Die grundlegende ideale Antwort besteht darin, dass diese Verwendung nicht nur auf den Namespace verweist und Aliase für den Namespace festlegt, sondern auch die Wiederverwendung nicht verwalteter Objektressourcen wie den Try-Finally-Block realisiert. Nur eine einfache Art, es zu schreiben.
Wenn Sie die Dispose-Methode verwenden, um ein nicht verwaltetes Objekt freizugeben, sollten Sie GC.SuppressFinalize aufrufen. Wenn sich das Objekt in der Finalisierungswarteschlange befindet, verhindert GC.SuppressFinalize, dass der GC die Finalize-Methode aufruft. Denn der Aufruf der Finalize-Methode führt zu Leistungseinbußen. Wenn Ihre Dispose-Methode die delegierten Ressourcen bereits bereinigt hat, muss der GC die Finalize-Methode (MSDN) des Objekts nicht erneut aufrufen. Im Anhang finden Sie den MSDN-Code als Referenz.
Code
öffentliche Klasse BaseResource: IDisposable
{
//Zeigen Sie auf externe nicht verwaltete Ressourcen
privates IntPtr-Handle;
// Andere verwaltete Ressourcen, die von dieser Klasse verwendet werden.
private Komponentenkomponenten;
// Verfolgen Sie, ob die .Dispose-Methode aufgerufen wird, markieren Sie das Bit und steuern Sie das Verhalten des Garbage Collectors
privater Bool entsorgt = false;
//Konstrukteur
öffentliche BaseResource()
{
// Fügen Sie hier den entsprechenden Konstruktorcode ein.
}
// Implementieren Sie die Schnittstelle IDisposable.
// Kann nicht als virtuelle Methode virtual deklariert werden.
// Unterklassen können diese Methode nicht überschreiben.
public void Dispose()
{
Dispose( true );
//Verlassen Sie die Finalisierungswarteschlange
//Legen Sie den blockierenden Finalizer-Code des Objekts fest
//
GC.SuppressFinalize( this );
}
// Dispose(bool disposing) wird in zwei verschiedenen Situationen ausgeführt.
// Wenn disposing gleich true ist, wurde die Methode aufgerufen
// Oder indirekt durch Benutzercode aufgerufen. Sowohl verwalteter als auch nicht verwalteter Code können freigegeben werden
// Wenn disposing gleich false ist, wurde die Methode intern vom Finalizer aufgerufen.
// Sie können keine anderen Objekte referenzieren, nur nicht verwaltete Ressourcen können freigegeben werden.
protected virtual void Dispose( bool entsorgen)
{
// Prüfen, ob Dispose aufgerufen wurde.
if ( ! this .disposed)
{
// Wenn gleich true, alle verwalteten und nicht verwalteten Ressourcen freigeben
wenn (entsorgen)
{
// Verwaltete Ressourcen freigeben.
Components.Dispose();
}
// Nicht verwaltete Ressourcen freigeben, wenn die Entsorgung falsch ist,
// Nur der folgende Code wird ausgeführt.
CloseHandle(Handle);
handle = IntPtr.Zero;
// Beachten Sie, dass dies nicht threadsicher ist.
// Nachdem die verwaltete Ressource freigegeben wurde, können andere Threads gestartet werden, um das Objekt zu zerstören.
// Aber vorher wird das Disponed-Flag auf true gesetzt
// Wenn Thread-Sicherheit erforderlich ist, muss der Client diese implementieren.
}
entsorgt = wahr;
}
//Verwenden Sie Interop, um die Methode aufzurufen
// Nicht verwaltete Ressourcen löschen.
[System.Runtime.InteropServices.DllImport( " Kernel32 " )]
private extern static Boolean CloseHandle(IntPtr handle);
//Verwenden Sie den C#-Destruktor, um den Finalizer-Code zu implementieren
// Dies kann nur aufgerufen und ausgeführt werden, wenn die Dispose-Methode nicht aufgerufen wurde.
// Wenn Sie der Basisklasse eine Chance zum Abschluss geben.
// Stellen Sie keinen Destruktor für Unterklassen bereit.
~BaseResource()
{
// Bereinigungscode nicht neu erstellen.
// Aus Gründen der Zuverlässigkeit und Wartbarkeit ist der Aufruf von Dispose(false) der beste Weg
Dispose( false );
}
// Ermöglicht Ihnen, die Dispose-Methode mehrmals aufzurufen,
// Es wird jedoch eine Ausnahme ausgelöst, wenn das Objekt freigegeben wurde.
// Unabhängig davon, wann Sie das Objekt verarbeiten, prüfen Sie, ob das Objekt freigegeben wird.
// Überprüfen Sie, ob es entsorgt wurde.
public void DoSomething()
{
if (this .disposed)
{
throw new ObjectDisposedException();
}
}
Für Typen, bei denen der Aufruf der Close-Methode natürlicher ist als der Aufruf der Dispose-Methode, können Sie der Basisklasse eine Close-Methode hinzufügen.
Die Close-Methode akzeptiert keine Parameter und ruft die Dispose-Methode auf, die entsprechende Bereinigungsarbeiten durchführt.
Das folgende Beispiel veranschaulicht die Close-Methode.
// Setzen Sie die Methode nicht auf virtuell.
// Geerbte Klassen dürfen diese Methode nicht überschreiben
öffentliche Leere Close()
{
// Dispose-Parameter ohne Parameter aufrufen.
Entsorgen();
}
öffentliches statisches void Main()
{
//Fügen Sie hier den zu erstellenden Code ein
// und ein BaseResource-Objekt verwenden.
}