Das Folgende ist eine detaillierte Erklärung des Golang GC-Garbage Collection-Mechanismus aus der Kolumne Golang-Tutorial. Ich hoffe, dass es Freunden in Not hilfreich sein wird!
Bei der tatsächlichen Verwendung der Go-Sprache stieß ich auf einige scheinbar seltsame Speichernutzungsphänomene, weshalb ich beschloss, das Garbage-Collection-Modell der Go-Sprache zu untersuchen. Dieser Artikel fasst die Ergebnisse der Studie zusammen.
Es war einmal, dass die Speicherverwaltung ein großes Problem für Programmierer war, die Anwendungen entwickelten. In herkömmlichen Programmiersprachen auf Systemebene (hauptsächlich C/C++) müssen Programmierer den Speicher sorgfältig verwalten und die Anwendung und Freigabe von Speicher steuern. Wenn Sie nicht aufpassen, kann es zu Speicherlecks kommen, die schwer zu finden und zu lokalisieren sind und für Entwickler schon immer ein Albtraum waren. Wie kann dieses Kopfschmerzproblem gelöst werden? In der Vergangenheit wurden im Allgemeinen zwei Methoden verwendet:
Um dieses Problem zu lösen, haben fast alle später entwickelten neuen Sprachen (Java, Python, PHP usw.) eine automatische Speicherverwaltung auf Sprachebene eingeführt – das heißt, Sprachbenutzer müssen nur darauf achten zu Speicheranwendungen und müssen sich nicht um die Speicherfreigabe kümmern, die Speicherfreigabe wird automatisch von der virtuellen Maschine oder der Laufzeit verwaltet. Dieses automatische Recycling von Speicherressourcen, die nicht mehr verwendet werden, wird als Garbage Collection bezeichnet.
Dies ist der einfachste Garbage-Collection-Algorithmus, der dem zuvor erwähnten Smart Pointer ähnelt. Pflegen Sie einen Referenzzähler für jedes Objekt. Wenn das Objekt, auf das verwiesen wird, zerstört oder aktualisiert wird, wird der Referenzzähler automatisch um eins verringert. Wenn das referenzierte Objekt erstellt oder einem anderen Objekt zugewiesen wird, wird der Referenzzähler automatisch erhöht um eins. Wenn der Referenzzähler 0 erreicht, wird das Objekt sofort recycelt.
Der Vorteil dieser Methode besteht darin, dass sie einfach zu implementieren ist und der Speicher zeitnah recycelt wird. Dieser Algorithmus wird häufig in Systemen mit knappem Speicher und hoher Echtzeitleistung verwendet, wie z. B. iOS Cocoa Framework, PHP, Python usw. Der einfache Referenzzählalgorithmus hat auch offensichtliche Nachteile:
Diese Methode ist in zwei Schritte unterteilt: Die Markierung beginnt bei der Stammvariablen und durchläuft alle referenzierten Objekte, auf die über die Anwendungsdurchquerung zugegriffen werden kann. Nachdem die Markierung abgeschlossen ist , wird der Löschvorgang durchgeführt und der nicht markierte Speicher wird recycelt (das Recycling kann von einem Defragmentierungsvorgang begleitet sein). Diese Methode behebt die Mängel der Referenzzählung, weist jedoch auch ein offensichtliches Problem auf: Jedes Mal, wenn die Garbage Collection gestartet wird, wird die gesamte aktuelle normale Codeausführung angehalten, und das Recycling verringert die Reaktionsfähigkeit des Systems erheblich! Natürlich sind viele Varianten des Mark&Sweep-Algorithmus (z. B. die Dreifarbenmarkierungsmethode) entstanden, um dieses Problem zu optimieren.
Nach vielen tatsächlichen Beobachtungen wissen wir, dass in objektorientierten Programmiersprachen der Lebenszyklus der meisten Objekte sehr kurz ist. Die Grundidee der Generationensammlung besteht darin, den Heap in zwei oder mehr Räume, sogenannte Generationen, aufzuteilen. Neu erstellte Objekte werden in der sogenannten jungen Generation gespeichert (im Allgemeinen ist die Größe der jungen Generation viel kleiner als die der alten Generation). Durch die wiederholte Ausführung der Speicherbereinigung werden Objekte mit längerem Lebenszyklus gefördert (Förderung). ) an die alte Generation. Daher sind zwei verschiedene Garbage-Collection-Methoden entstanden, die Garbage-Collection der neuen Generation und die Garbage-Collection der alten Generation, mit denen die Garbage Collection für Objekte in ihren jeweiligen Räumen durchgeführt wird. Die Geschwindigkeit der Garbage Collection ist in der neuen Generation sehr hoch, um mehrere Größenordnungen schneller als in der alten Generation. Auch wenn die Häufigkeit der Garbage Collection in der neuen Generation höher ist, ist die Ausführungseffizienz immer noch besser als in der alten Da der Lebenszyklus der meisten Objekte sehr kurz ist, besteht überhaupt keine Notwendigkeit, sie auf die alte Generation umzustellen.
Go Language Garbage Collection verwendet im Allgemeinen den klassischen Mark-and-Sweep-Algorithmus.
Das häufigste und schwierigste Problem, auf das das Team beim Üben der Go-Sprache stößt, ist das Gedächtnisproblem (hauptsächlich GC). Hier ist eine Zusammenfassung der aufgetretenen Probleme und Erfahrungen .
Dieses Problem wurde entdeckt, als wir einen Stresstest für den Hintergrunddienst durchführten. Zu diesem Zeitpunkt simulierten wir eine große Anzahl von Benutzeranfragen für den Zugriff auf den Hintergrunddienst konnte einen deutlichen Anstieg der Speichernutzung beobachten. Beim Beenden des Stresstests sank die Speichernutzung jedoch nicht wesentlich. Es dauerte lange, das Problem mit verschiedenen Methoden wie gprof zu lokalisieren, aber es wurde immer noch keine Ursache gefunden. Schließlich fand ich heraus, dass es zu diesem Zeitpunkt normal war ... Es gibt zwei Hauptgründe,
Erstens verfügt die Garbage Collection von Go über einen Auslöseschwellenwert, der mit zunehmender Speichernutzung schrittweise ansteigt (z. B. beträgt der anfängliche Schwellenwert 10 MB, beim nächsten Mal sind es 20 MB und beim nächsten Mal sind es 40 MB ... ), wenn gc go längere Zeit nicht ausgelöst wird, wird es einmal (2 Minuten) aktiv ausgelöst. Wenn die Speichernutzung während der Spitzenzeiten ansteigt, ist es fast unmöglich, GC basierend auf dem Schwellenwert auszulösen, es sei denn, Sie beantragen weiterhin Speicher. Stattdessen müssen Sie bis zu 2 Minuten warten, bis der aktive GC startet, bevor Sie GC auslösen.
Der zweite Grund besteht darin, dass die Go-Sprache dem System lediglich mitteilt, dass der Speicher nicht mehr benötigt wird und recycelt werden kann. Gleichzeitig übernimmt das Betriebssystem eine „Verzögerungs“-Strategie , nicht um es sofort zu recyceln, sondern um zu warten, bis der Systemspeicher knapp wird. Erst dann beginnt das Recycling, sodass das Programm eine extrem schnelle Zuweisungsgeschwindigkeit erreichen kann.
Für Back-End-Programme, bei denen Benutzer auf Ereignisse reagieren müssen, ist es ein Albtraum, die Welt während Golang GC in Teilzeit anzuhalten. Gemäß der obigen Einführung wird die GC-Leistung von Version 1.5 nach Abschluss der oben genannten Verbesserungen erheblich verbessert. Allerdings werden alle Garbage Collection-Sprachen während des GC unweigerlich mit Leistungseinbußen konfrontiert sein Erstellen Sie häufig temporäre Objekte (z. B. &abc{}, new, make usw.), um die Scanzeit während der Speicherbereinigung zu verkürzen. Erwägen Sie die Wiederverwendung temporärer Objekte, die häufig über den Array-Cache verwendet werden Verwenden Sie die CGO-Methode, um den Speicher selbst zu verwalten und die Garbage Collection zu umgehen. Diese Methode wird nicht empfohlen, es sei denn, sie kann leicht zu unvorhersehbaren Problemen führen Diese Methode ist immer noch sehr offensichtlich~
uns Ein Dienst muss viele lange Verbindungsanforderungen verarbeiten. Für jede lange Verbindungsanforderung wird eine Lese- und Schreib-Coroutine sowie eine Endlos-for-Schleife geöffnet dient der kontinuierlichen Verarbeitung der Sende- und Empfangsdaten. Wenn die Verbindung vom Remote-Ende geschlossen wird und diese beiden Coroutinen nicht verarbeitet werden, laufen sie weiterhin weiter und die belegten Kanäle werden nicht freigegeben ... Sie müssen hier sehr vorsichtig sein und sie nach Nichtgebrauch entfernen Die Coroutinen schließen den abhängigen Kanal und bestimmen, ob der Kanal in der Coroutine geschlossen ist, um seinen Ausgang sicherzustellen.
30. April 2016, 20:02 Uhr | KOMMENTARE
In diesem Teil werden hauptsächlich einige Einführungskenntnisse zu Golang-GC vorgestellt.
Die Hauptreferenz ist diese:
http://morsmachine.dk/machine-gc
Es wurde 2014 geschrieben. Es wird geschätzt, dass der GC-Mechanismus zu dieser Zeit relativ einfach war. Die neue Version von Golang Die Änderungen an GC sollten relativ groß sein. aber tatsächlich habe ich die genaue Bedeutung nie gesehen.
SpeicherverlustWenn das Programm einen Teil des Speichers im Speicherbereich beantragt und der Speicherplatz nach der Ausführung des Programms nicht freigegeben wird und das entsprechende Programm nicht über einen guten GC-Mechanismus verfügt, um den vom Programm angewendeten Speicherplatz wiederzuverwenden Dies führt zu Speicherverlusten.
Aus Sicht des Benutzers verursacht der Speicherverlust selbst keinen Schaden, da er die Benutzerfunktionen nicht beeinträchtigt, aber wenn ein „Speicherverlust“ auftritt Für Sprachen wie C und C++, die keine Garbage Collection haben Wir konzentrieren uns hauptsächlich auf zwei Arten von Speicherlecks:
Heap-Lecks. Speicher bezieht sich auf einen Teil des Speichers, der nach Bedarf beim Ausführen des Programms über Malloc, Realloc New usw. vom Heap zugewiesen wird. Nach Abschluss muss er durch Aufrufen der entsprechenden Funktion „Free“ oder „Delete“ gelöscht werden. Wenn der Entwurfsfehler des Programms dazu führt, dass dieser Teil des Speichers nicht freigegeben wird, wird dieser Speicher in Zukunft nicht mehr verwendet und es kommt zu einem Heap-Leck.Systemressourcenleck (Ressourcenleck) Bezieht sich hauptsächlich auf Das Programm verwendet vom System zugewiesene Ressourcen wie Bitmap, Handle, SOCKET usw. und wird nicht mit den entsprechenden Funktionen freigegeben, was zu einer Verschwendung von Systemressourcen führt, was zu einer ernsthaften Verringerung der Systemleistung und einem instabilen Systembetrieb führen kann.Hier finden Sie Informationen zu spezifischen Vor- und Nachteilen. Hier finden Sie nur eine allgemeine Einführung.
Der Hauptzweck dieses Schritts besteht darin, die Statusinformationen dieser Objekte zu erhalten.
gc sammelt nur Objekte, die nicht als erreichbar markiert sind. Wenn gc eine Referenz nicht erkennt, wird möglicherweise ein noch verwendetes Objekt recycelt, was zu einem Fehler bei der Programmausführung führt.
Sie können die drei Hauptschritte sehen: Scannen, Recycling und Reinigen. Ich denke, dass das Garbage-Collection-Modell in Golang im Vergleich zu anderen Sprachen relativ einfach ist.
Probleme in gcgc kann als eingeführt werden, um das Problem des Speicherrecyclings zu lösen. Bei der Verwendung neu entwickelter Sprachen (Java, Python, PHP usw.) müssen sich Benutzer nicht um die Freigabe von Speicherobjekten kümmern, sondern müssen sich nur um die Anwendung von Objekten kümmern, indem sie verwandte Vorgänge zur Laufzeit oder in der VM ausführen , Um den Effekt der automatischen Verwaltung des Speicherplatzes zu erzielen, wird dieses Verhalten des automatischen Recyclings nicht mehr verwendeter Speicherressourcen als Garbage Collection bezeichnet.
Nach der vorherigen Aussage ist die Frage, ob GC eine Referenz richtig identifizieren kann, die Grundlage dafür, dass GC normal funktioniert. Daher lautet die erste Frage: Wie sollte GC eine Referenz identifizieren?
Das größte Problem: Es ist schwierig, Referenzen zu identifizieren. Es ist schwierig zu wissen, was ein Maschinencode ist und was eine Referenz darstellt. Wenn versehentlich eine Referenz übersehen wird, wird jetzt versehentlich Speicher freigegeben, der nicht zur Freigabe bereit war. Daher besteht die Strategie darin, sich auf die Seite von mehr statt von weniger zu entscheiden.
Eine Strategie besteht darin, alle Speicherplätze als mögliche Referenzen (Zeigerwerte) zu behandeln. Dies wird als „konservativer Müllsammler“ (konservativer Müllsammler) bezeichnet. So funktioniert der Boehm-Garbage Collector in C. Das heißt, gewöhnliche Variablen im Speicher werden wie Zeiger behandelt und versuchen, alle Zeiger abzudecken. Wenn sich zufällig andere Objekte im Raum befinden, auf die der gewöhnliche Variablenwert zeigt, wird dieses Objekt nicht recycelt. Die Go-Sprachimplementierung kennt die Typinformationen des Objekts vollständig und durchläuft beim Markieren nur das Objekt, auf das der Zeiger zeigt, wodurch die Verschwendung von Heap-Speicher in der C-Implementierung vermieden wird (Auflösung etwa 10–30 %). Dreifarbige Markierung
2015/8 1.5 Einführung der dreifarbigen Markierung
Informationen zur Einführung der gleichzeitigen Reinigung finden Sie hier in In Version 1.3 trennt die Go-Laufzeit wie zuvor die Ausführung aller Aufgaben und startet die Markierung (der Markierungsteil erfordert immer noch das Anhalten des ursprünglichen Programms, nachdem die Markierung abgeschlossen ist). Starten Sie die Aufgabe sofort neu und lassen Sie die Sweep-Aufgabe wie eine normale Coroutine-Aufgabe parallel laufen und zusammen mit anderen Aufgaben ausführen. Bei der Ausführung auf einem Multi-Core-Prozessor versucht go, die GC-Aufgabe auf einem separaten Kern auszuführen, ohne die Ausführung des Geschäftscodes zu beeinträchtigen. Das Go-Team selbst gibt an, dass dadurch die Pausenzeit um 50 % bis 70 % verkürzt wird.
Der grundlegende Algorithmus ist das zuvor erwähnte Reinigen + Recycling. Der Kern der Golang-GC-Optimierung besteht darin, die STW-Zeit (Stop The World) immer kürzer zu machen.
Wie misst man GC?
GODEBUG=gctrace=1 ./myserver
aus Wenn Sie die Ausgabeergebnisse verstehen möchten, müssen Sie eine weitere eingehende Analyse der GC-Prinzipien durchführen. Der Vorteil dieses Artikels besteht darin, dass er klar zeigt, welche Faktoren die GC-Zeit von Golang bestimmen kann auch verschiedene gezielte Maßnahmen ergreifen: Gemäß der vorherigen Analyse können wir auch wissen, dass GC in Golang die Clear-Mark-Methode verwendet, sodass die Gesamtzeit von GC ist: GODEBUG=gctrace=1 ./myserver
,如果要想对于输出结果了解,还需要对于 gc 的原理进行更进一步的深入分析,这篇文章的好处在于,清晰的之处了 golang 的 gc 时间是由哪些因素决定的,因此也可以针对性的采取不同的方式提升 gc 的时间:
根据之前的分析也可以知道,golang 中的 gc 是使用标记清楚法,所以 gc 的总时间为:
Tgc = Tseq + Tmark + Tsweep
Tgc = Tseq + Tmark + Tsweep
(T steht für Zeit)Wie die GOGC-Parameter eingestellt werden, sollte entsprechend dem tatsächlichen Produktionsszenario festgelegt werden, z. B. durch Erhöhen der GOGC-Parameter, um die GC-Häufigkeit zu verringern. Tipps
Wenn Sie detaillierte Einblicke erhalten möchten, ist die Verwendung von gdb unerlässlich. In diesem Artikel wurden einige Einführungstipps für die Verwendung von gdb zusammengestellt.
Objektzuordnung reduzierenDie sogenannte Reduzierung der Objektzuordnung bedeutet eigentlich, Objekte so weit wie möglich wiederzuverwenden. Zum Beispiel die folgenden zwei Funktionsdefinitionen:
Die erste Funktion weist bei jedem Aufruf ein Leerzeichen zu, was zusätzlichen Druck auf gc ausübt. Die zweite Funktion verwendet die formale Parameterdeklaration bei jedem Aufruf wieder.
Ein alltägliches Gespräch über String- und []Byte-Konvertierung Die Konvertierung zwischen Stirng und []Byte wird Druck auf gc ausüben. Sie können zunächst die Datenstrukturen der beiden vergleichen:
Wenn die beiden konvertiert werden, wird die zugrunde liegende Datenstruktur kopiert, sodass die GC-Effizienz geringer wird. In Bezug auf die Lösungsstrategie besteht eine Möglichkeit darin, immer [] Byte zu verwenden, insbesondere im Hinblick auf die Datenübertragung. [] Byte enthält auch viele effektive Operationen, die häufig in Zeichenfolgen verwendet werden. Die andere besteht darin, Vorgänge auf niedrigerer Ebene zum direkten Konvertieren zu verwenden, um ein Kopieren zu vermeiden. Sie können sich auf den ersten Teil der Leistungsoptimierung in WeChat „Yuhen Academy“ beziehen, der hauptsächlich unsicher.Pointer für die direkte Konvertierung verwendet.
Zur Verwendung von unsicher kann ich meiner Meinung nach einen separaten Artikel verfassen. Listen Sie zunächst die relevanten Informationen hier auf: http://studygolang.com/articles/685 Intuitiv kann unsafe.Pointer als c++ void* verstanden werden Golang entspricht einer Brücke zur Konvertierung verschiedener Zeigertypen.
Der zugrunde liegende Typ von uintptr ist int, der den Wert der Adresse enthalten kann, auf die der Zeiger zeigt. Es kann in und aus unsafe.Pointer konvertiert werden. Der Hauptunterschied besteht darin, dass uintptr an Zeigeroperationen teilnehmen kann, während unsafe.Pointer nur eine Zeigerkonvertierung und keine Zeigeroperationen durchführen kann. Wenn Sie Golang für die Zeigerarithmetik verwenden möchten, können Sie darauf verweisen. Bei der Durchführung bestimmter Zeigeroperationen muss dieser zunächst in den Typ uintptr konvertiert werden, bevor weitere Berechnungen durchgeführt werden können, beispielsweise der Offset usw.
Verwenden Sie + sparsam, um Zeichenfolgen zu verbinden. Da die Verwendung von + zum Verbinden von Zeichenfolgen neue Objekte generiert und die Effizienz von gc verringert, ist die Verwendung der Append-Funktion am besten.
Aber es gibt noch einen weiteren Nachteil. Sehen Sie sich zum Beispiel den folgenden Code an:
Nach Verwendung der Anhängeoperation erhöht sich der Speicherplatz des Arrays von 1024 auf 1312. Wenn Sie also die Länge des Arrays im Voraus kennen können, ist dies der Fall Es ist am besten, ihn bei der anfänglichen Zuweisung des Speicherplatzes zuzuweisen. Eine gute Arbeit bei der Raumplanung erhöht die Kosten für die Codeverwaltung und verringert gleichzeitig den Druck auf GC und verbessert die Codeeffizienz.
Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung des Garbage-Collection-Mechanismus von Golang GC. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!