Heim  >  Artikel  >  Backend-Entwicklung  >  .Net Garbage Collection und Verarbeitung großer Objekte

.Net Garbage Collection und Verarbeitung großer Objekte

黄舟
黄舟Original
2017-02-17 11:17:371306Durchsuche

Englischer Originaltext: Maoni Stephens, zusammengestellt von: Zhao Yukai (@玉凯Sir)

Der CLR-Garbage Collector unterteilt Objekte nach der Größe von den Raum, den sie einnehmen. Es gibt einen großen Unterschied im Umgang mit großen und kleinen Objekten. Beispiel: Speicherdefragmentierung – das Verschieben großer Objekte im Speicher ist teuer. Sehen wir uns an, wie der Garbage Collector mit großen Objekten umgeht und welche potenziellen Auswirkungen große Objekte auf die Programmleistung haben.

Heap und Garbage Collection für große Objekte

Wenn in .Net 1.0 und 2.0 die Größe eines Objekts 85.000 Byte überschreitet, wird es als A betrachtet großes Objekt. Diese Zahl basiert auf Erfahrungen mit der Leistungsoptimierung. Wenn die von einem Objekt angeforderte Speichergröße diesen Schwellenwert erreicht, wird sie dem großen Objektheap zugewiesen. Was bedeutet das? Um dies zu verstehen, müssen wir den .Net-Garbage-Collection-Mechanismus verstehen.

Wie die meisten Menschen wissen, sammelt .Net GC Daten in „Generationen“. Es gibt drei Generationen von Objekten im Programm: Generation 0, Generation 1 und Generation 2. Generation 0 ist das jüngste Objekt und Objekte der Generation 2 haben die längste Überlebenszeit. GC sammelt aus Leistungsgründen den Müll nach Generation; normalerweise werden Objekte in Generation 0 recycelt. Beispielsweise sollten in einem ASP.NET-Programm mit jeder Anforderung verknüpfte Objekte am Ende der Anforderung recycelt werden. Objekte, die nicht recycelt wurden, werden zu Objekten der Generation 1; das heißt, Objekte der Generation 1 sind ein Puffer zwischen residenten Speicherobjekten und Objekten, die bald sterben.

Aus Generationensicht gehören große Objekte zu Objekten der Generation 2, da große Objekte nur beim Recycling der Generation 2 verarbeitet werden. Wenn eine Garbage Collection einer bestimmten Generation ausgeführt wird, wird gleichzeitig die Garbage Collection der jüngeren Generation ausgeführt. Beispiel: Wenn die Garbage Collection der 1. Generation durchgeführt wird, werden die Objekte der 1. Generation und der 0. Generation gleichzeitig gesammelt. Wenn die Garbage Collection der 2. Generation durchgeführt wird, werden die Objekte der 1. Generation und der 0. Generation gleichzeitig gesammelt Bei der

-Generierung unterscheidet der Garbage Collector die logische Ansicht. Aus Sicht des physischen Speichers werden Objekte auf verschiedenen verwalteten Heaps zugewiesen. Ein verwalteter Heap ist ein Speicherbereich, der vom Garbage Collector vom Betriebssystem zugewiesen wird (durch Aufruf der Windows-API VirtualAlloc). Wenn die CLR Speicher lädt, initialisiert sie zwei verwaltete Heaps, einen Heap für große Objekte (LOH – Large Object Heap) und ein kleines Objektpaar (SOH – Small Object Heap).

Eine Speicherzuweisungsanforderung besteht darin, das verwaltete Objekt auf dem entsprechenden verwalteten Heap zu platzieren. Wenn die Größe des Objekts weniger als 85.000 Byte beträgt, wird es in SOH platziert; andernfalls wird es in LOH platziert.

Für SOH tritt das Objekt nach der Durchführung einer Garbage Collection in die nächste Generation ein. Das heißt, wenn das überlebende Objekt bei der ersten Garbage Collection in die zweite Generation eintritt und das Objekt nach der zweiten Garbage Collection immer noch nicht Garbage Collection ist, wird es zu einem Objekt der zweiten Generation Das Objekt ist das älteste Objekt und erhöht die Generation nicht.

Wenn die Garbage Collection ausgelöst wird, defragmentiert der Garbage Collector den Heap kleiner Objekte und verschiebt die verbleibenden Objekte zusammen. Aufgrund der hohen Kosten für das Verschieben des Speichers entschied sich das CLR-Team dafür, diese nur zu löschen und eine Liste recycelter Objekte zu erstellen, um die nächste große Objektanforderung zur Speichernutzung zu erfüllen. Benachbarte Müllobjekte werden in A zusammengeführt freier Speicherblock.

Es sollte immer beachtet werden, dass der Heap großer Objekte erst mit .Net 4.0 defragmentiert wird, dies kann jedoch in Zukunft geschehen. Wenn Sie also große Objekte zuordnen möchten und nicht möchten, dass diese verschoben werden, können Sie die Fixed-Anweisung verwenden.

Das folgende Diagramm zeigt das Recycling kleiner Objekthaufen SOH



Vor der ersten Müllabfuhr in das obige Bild Es gibt vier Objekte obj0-3; nach der ersten Garbage Collection wurden obj1 und obj3 recycelt, und vor der zweiten Garbage Collection wurden drei Objekte obj4-6 zugewiesen; Nach der ersten Garbage Collection wurden obj2 und obj5 recycelt und obj4 und obj6 neben obj0 verschoben.

Das Bild unten ist ein schematisches Diagramm des LOH-Recyclings großer Objekthaufen



Das können Sie sehen Die Garbage Collection wurde nicht durchgeführt. Zuvor gab es vier Objekte obj0-3; nach der ersten Garbage Collection wurden obj1 und obj2 recycelt. Nach dem Recycling wurden die von obj1 und obj2 belegten Räume zusammengeführt Zuweisung, obj1 Der nach dem Recycling freigegebene Speicherplatz und die Zuweisung von obj2 lassen gleichzeitig ein Speicherfragment übrig. Wenn die Größe dieses Fragments weniger als 85.000 Byte beträgt, kann dieses Fragment während des Lebenszyklus dieses Programms nie wieder verwendet werden.

Wenn auf dem Heap für große Objekte nicht genügend freier Speicher vorhanden ist, um den zu beantragenden großen Objektraum aufzunehmen, versucht die CLR zunächst, Speicher vom Betriebssystem zu beantragen. Wenn die Anwendung fehlschlägt, wird dies der Fall sein Lösen Sie ein Recycling der zweiten Generation aus, um zu versuchen, etwas Speicher freizugeben.

Während der Garbage Collection der 2. Generation kann über VirtualFree unnötiger Speicher an das Betriebssystem zurückgegeben werden. Bitte sehen Sie sich das Bild unten für den Rückgabevorgang an:



Wann werden große Gegenstände recycelt?

Bevor wir besprechen, wann große Objekte recycelt werden sollten, werfen wir zunächst einen Blick darauf, wann normale Garbage-Collection-Vorgänge durchgeführt werden. Die Garbage Collection erfolgt unter den folgenden Umständen:

1. Der angeforderte Speicherplatz überschreitet die Speichergröße der Generation 0 oder den Schwellenwert des großen Objektheaps.

2. Wenn Sie die GC.Collect-Methode im Programmcode aufrufen, wird beim Aufruf der GC.Collect-Methode der Parameter GC.MaxGeneration übergeben, einschließlich der Garbage Collection des großen Objektheaps 🎜>

3. Wenn das Betriebssystem nicht über genügend Speicher verfügt, wenn die Anwendung eine Benachrichtigung über hohen Speicher vom Betriebssystem erhält

4. Wenn der Garbage-Collection-Algorithmus davon ausgeht, dass das Recycling der zweiten Generation effektiv ist, wird dies getan löst die Speicherbereinigung der zweiten Generation aus

5. Jede Generation des Objektheaps hat ein Attribut, das einen Schwellenwert für die Speicherplatzgröße einnimmt. Wenn Sie Objekte einer bestimmten Generation zuweisen, erhöhen Sie die Gesamtspeichermenge in der Nähe des Schwellenwerts dieser Generation oder weisen Sie Objekte zu, die diese Generation verursachen. Wenn die Heap-Größe den Heap-Schwellenwert überschreitet, wird eine Speicherbereinigung durchgeführt. Wenn Sie also ein kleines oder großes Objekt zuweisen, verbraucht es den Schwellenwert des Heaps der Generation 0 oder des Heaps großer Objekte. Wenn der Garbage Collector die Objektgeneration auf Generation 1 oder Generation 2 erhöht, wird der Schwellenwert der Generationen 1 und 2 verbraucht. Diese Schwellenwerte ändern sich dynamisch, während das Programm ausgeführt wird.

Auswirkungen des Heaps großer Objekte auf die Leistung

Lassen Sie uns zunächst einen Blick auf die Kosten für die Zuweisung großer Objekte werfen. Wenn die CLR jedem neuen Objekt Speicher zuweist, muss sie sicherstellen, dass der Speicher gelöscht und nicht von anderen Objekten verwendet wird (ich gebe aus, dass er gelöscht ist). Dies bedeutet, dass die Kosten der Zuweisung vollständig durch die Kosten der Bereinigung gesteuert werden (es sei denn, während der Zuweisung wird eine Speicherbereinigung ausgelöst). Wenn zum Löschen eines Bytes zwei Zyklen erforderlich sind, bedeutet dies, dass zum Löschen eines kleinsten großen Objekts 170.000 Zyklen erforderlich sind. Normalerweise weisen Benutzer keine sehr großen Objekte zu. Beispielsweise dauert das Zuweisen eines 16-Megapixel-Objekts auf einem 2-GHz-Computer etwa 16 ms, um den Speicher zu löschen. Der Preis ist zu hoch.

Werfen wir einen Blick auf die Recyclingkosten. Wie bereits erwähnt, werden große Objekte zusammen mit Objekten der 2. Generation recycelt. Wenn der von einem großen Objekt oder einem Objekt der zweiten Generation eingenommene Platz seinen Schwellenwert überschreitet, wird das Recycling des Objekts der zweiten Generation ausgelöst. Wenn das Recycling der Generation 2 ausgelöst wird, weil der große Objektheap den Schwellenwert überschreitet, gibt es im Objektheap der Generation 2 selbst nicht viele Objekte, die recycelt werden können. Dies ist kein großes Problem, wenn sich auf dem Heap der 2. Generation nicht viele Objekte befinden. Wenn der Heap der zweiten Generation jedoch groß ist und viele Objekte enthält, führt ein übermäßiges Recycling der zweiten Generation zu Leistungsproblemen. Wenn Sie große Objekte vorübergehend zuweisen, nimmt die Ausführung der Speicherbereinigung viel Zeit in Anspruch. Wenn Sie also weiterhin große Objekte verwenden und diese dann freigeben, wirkt sich dies stark negativ auf die Leistung aus.

Riesige Objekte auf dem Heap großer Objekte sind normalerweise Arrays (es kommt selten vor, dass ein Objekt sehr groß ist). Wenn es sich bei den Elementen im Objekt um starke Referenzen handelt, sind die Kosten sehr hoch. Wenn zwischen den Elementen keine gegenseitigen Referenzen vorhanden sind, muss bei der Speicherbereinigung nicht das gesamte Array durchlaufen werden. Beispiel: Verwenden Sie ein Array, um die Knoten eines Binärbaums zu speichern. Eine Möglichkeit besteht darin, den linken und rechten Knoten im Knoten stark zu referenzieren:

class Node
{
Data d;
Node left;
Node right;
}
 
Node[] binaryTree = new Node[num_nodes];

Wenn num_nodes eine große Zahl ist, ist dies der Fall bedeutet, dass jeder Knoten mindestens zwei Referenzelemente anzeigen muss. Eine Alternative besteht darin, die Array-Indexnummern der linken und rechten Knotenelemente im Knoten


class Node
{
Data d;
uint left_index;
uint right_index;
}

zu speichern. In diesem Fall handelt es sich um die Referenzbeziehung zwischen den Elementen wird entfernt; Sie können den referenzierten Knoten über BinaryTree[left_index] abrufen. Der Garbage Collector muss bei der Garbage Collection nicht mehr auf verwandte Referenzelemente achten.

Leistungsdaten für große Objekt-Heaps sammeln

Es gibt verschiedene Möglichkeiten, Leistungsdaten im Zusammenhang mit großen Objekt-Heaps zu sammeln. Bevor ich diese Methoden erläutere, wollen wir darüber sprechen, warum Sie Leistungsdaten im Zusammenhang mit großen Objekt-Heaps sammeln müssen.

Wenn Sie mit der Erfassung von Leistungsdaten zu einem bestimmten Aspekt beginnen, haben Sie möglicherweise bereits Hinweise darauf gefunden, dass dieser Aspekt einen Leistungsengpass verursacht, oder Sie haben möglicherweise nicht alle Aspekte durchsucht und kein Problem gefunden.

Die Leistungsindikatoren für den .Net CLR-Speicher sind normalerweise das erste Tool, das Sie bei der Suche nach Leistungsproblemen in Betracht ziehen sollten. Zu den mit LOH verbundenen Zählern gehören Sammlungen der Generation 2 (Anzahl der Heap-Sammlungen der Generation 2) und große Objekt-Heap-Größen. Sammlungen der Generation 2 zeigen die Anzahl der Garbage Collection-Vorgänge der Generation 2 an, die seit dem Start des Prozesses stattgefunden haben. Der Zähler für die Größe des Heapspeichers für große Objekte zeigt die aktuelle Größe des Heapspeichers für große Objekte an, einschließlich des freien Speicherplatzes. Dieser Zähler wird nach jedem Speicherbereinigungsvorgang aktualisiert, nicht jedes Mal, wenn Speicher zugewiesen wird.

Sie können sich auf die Abbildung unten beziehen, um die Leistungsdaten im Zusammenhang mit dem .Net CLR-Speicher im Windows-Leistungsindikator zu beobachten


Sie können die Werte dieser Zähler auch über Programme abfragen. Viele Leute sammeln Leistungsindikatoren über Programme, um Leistungsengpässe zu finden.

Natürlich können Sie auch den Debugger winddbg verwenden, um den großen Objekthaufen zu beobachten.

Letzte Erinnerung: Bisher wird der Heap großer Objekte nicht im Rahmen der Garbage Collection defragmentiert, dies ist jedoch nur ein Implementierungsdetail von clr, und Programmcode sollte nicht auf dieser Funktion basieren. Wenn Sie sicherstellen möchten, dass das Objekt nicht vom Garbage Collector verschoben wird, verwenden Sie die Fixed-Anweisung.

Ursprüngliche Adresse: http://www.php.cn/

Das Obige ist die .Net-Garbage Collection Bezüglich des Inhalts der Verarbeitung großer Objekte beachten Sie bitte die chinesische PHP-Website (www.php.cn), um weitere verwandte Inhalte zu erhalten!


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