Heim  >  Artikel  >  Backend-Entwicklung  >  Detaillierte Erläuterung des Garbage-Collection-Mechanismus von Golang GC

Detaillierte Erläuterung des Garbage-Collection-Mechanismus von Golang GC

藏色散人
藏色散人nach vorne
2020-09-14 09:47:083266Durchsuche

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!

Detaillierte Erläuterung des Garbage-Collection-Mechanismus von Golang GC

Abstract

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.

Was ist Garbage Collection?

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:

  • Tools zur Erkennung von Speicherlecks. Das Prinzip dieses Tools ist im Allgemeinen das statische Code-Scannen, das Codesegmente erkennt, die zu Speicherverlusten durch den Scanner führen können. Erkennungstools weisen jedoch zwangsläufig Lücken und Mängel auf und können nur eine unterstützende Rolle spielen.
  • Intelligente Zeiger. Dies ist eine in C++ eingeführte automatische Speicherverwaltungsmethode, die über Zeigerobjekte mit automatischer Speicherverwaltungsfunktion referenziert wird. Programmierer müssen der Speicherfreigabe nicht allzu viel Aufmerksamkeit schenken und erreichen den Zweck der automatischen Speicherfreigabe. Diese Methode ist die am weitesten verbreitete Methode, verursacht jedoch für Programmierer einen gewissen Lernaufwand (sie bietet keine native Unterstützung auf Sprachebene) und Speicherverluste können nicht vermieden werden, wenn Sie einmal vergessen, sie zu verwenden.

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.

Gemeinsame Garbage-Collection-Methoden

Referenzzählung

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:

  • Eine häufige Aktualisierung des Referenzzählers verringert die Leistung. Eine einfache Lösung besteht darin, dass der Compiler benachbarte Referenzanzahl-Aktualisierungsvorgänge zu einem Update zusammenführt. Eine andere Methode besteht darin, häufige temporäre Variablenreferenzen nicht zu zählen, sondern den Stapel zu scannen, um zu bestätigen, ob die Referenz 0 erreicht. Es gibt auch temporäre Objektreferenzen, die bestimmen ob man sie freigibt. Es gibt viele andere Methoden. Weitere Informationen finden Sie hier.
  • Zirkuläres Referenzproblem. Wenn zwischen Objekten ein Zirkelverweis auftritt, können die Objekte in der Verweiskette nicht freigegeben werden. Die naheliegendste Lösung besteht darin, Zirkelverweise zu vermeiden. Cocoa führt beispielsweise zwei Zeigertypen ein: starker Zeiger und schwacher Zeiger. Oder das System erkennt Zirkelbezüge und unterbricht proaktiv die Zirkelkette. Dies erhöht natürlich auch die Komplexität der Garbage Collection.

Markieren und fegen

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.

Generationssammlung (Generierung)

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.

GOs Garbage Collector

Go Language Garbage Collection verwendet im Allgemeinen den klassischen Mark-and-Sweep-Algorithmus.

  • Vor Version 1.3 war der Garbage-Collection-Algorithmus von Golang sehr grob, und auch seine Leistung wurde vielfach kritisiert: Unter bestimmten Bedingungen (Speicher überschreitet einen Schwellenwert oder periodisch, z. B. 2 Minuten) pausiert Go Runtime die Ausführung aller Aufgaben und führt Mark&Sweep durch Operationen starten die Ausführung aller Aufgaben, nachdem die Operation abgeschlossen ist. In Szenarien, in denen viel Speicher verwendet wird, tritt beim Go-Programm beim Durchführen der Speicherbereinigung ein sehr offensichtliches Feststeckphänomen (Stop The World) auf. Bei Hintergrunddienstprozessen, die eine hohe Reaktionsgeschwindigkeit erfordern, ist eine solche Verzögerung einfach nicht tolerierbar! Während dieser Zeit waren viele Teams im In- und Ausland, die die Go-Sprache in Produktionsumgebungen praktizierten, mehr oder weniger auf die Fallstricke von GC gestoßen. Zu dieser Zeit bestand eine gängige Methode zur Lösung dieses Problems darin, die Menge des automatisch zugewiesenen Speichers so schnell wie möglich zu steuern, um die GC-Last zu reduzieren, und die manuelle Speicherverwaltung zu verwenden, um Szenarien zu bewältigen, die große Speichermengen und eine Hochfrequenzzuweisung erfordern.
  • Ab Version 1.3 begann das Go-Team, die GC-Leistung kontinuierlich zu verbessern und zu optimieren. Mit jeder neuen Version von Go stehen die GC-Verbesserungen im Mittelpunkt aller Aufmerksamkeit. In Version 1.3 trennt die Go-Laufzeit wie zuvor die Ausführung aller Aufgaben und startet die Markierung. Stattdessen werden die angehaltenen Aufgaben neu gestartet Die gewöhnliche Coroutine-Aufgabe wird parallel zu anderen Aufgaben ausgeführt. Bei der Ausführung auf einem Mehrkernprozessor versucht go, die GC-Aufgabe auf einem separaten Kern auszuführen, ohne die Ausführung des Geschäftscodes zu beeinträchtigen. Nach eigener Aussage des Go-Teams wurde die Pausenzeit um 50–70 % verkürzt.
  • Version 1.4 (die neueste stabile Version) weist nicht viele Leistungsänderungen an gc auf. In Version 1.4 hat ein Großteil des Laufzeitcodes die native C-Sprachimplementierung durch die Go-Sprachimplementierung ersetzt. Eine wesentliche Änderung bei gc besteht darin, dass es eine genaue GC erreichen kann. Die C-Sprachimplementierung kann während der GC nicht die Objektinformationen des Speichers abrufen und daher nicht genau zwischen gewöhnlichen Variablen und Zeigern unterscheiden. Sie kann gewöhnliche Variablen nur dann als Zeiger behandeln, wenn sich zufällig andere Objekte im Raum befinden, auf die diese gewöhnliche Variable zeigt , dann 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 %).
  • In Version 1.5 hat das Go-Team wesentliche Verbesserungen an GC vorgenommen (in 1.4 wurden Vorahnungen vorgenommen, beispielsweise die Einführung einer Schreibbarriere. Das offizielle Hauptziel besteht darin, Verzögerungen zu reduzieren). Der in Go 1.5 implementierte Garbage Collector ist ein „nicht-generationeller, nicht verschiebbarer, gleichzeitiger, dreifarbiger Mark-and-Sweep-Garbage Collector“. Der Generationsalgorithmus wurde oben erwähnt und ist eine relativ gute Strategie zur Speicherbereinigung. Ich vermute jedoch, dass die Schritte nicht zu groß sein dürfen und dass sie schrittweise verbessert werden müssen wird in der GC-Optimierung von Version 1.6 berücksichtigt. Gleichzeitig wird die oben vorgestellte dreifarbige Markierungsmethode eingeführt. Der Markierungsvorgang dieser Methode kann schrittweise ausgeführt werden, ohne jedes Mal den gesamten Speicherplatz zu scannen, was die Zeit zum Stoppen der Welt verkürzen kann. Daraus ist ersichtlich, dass sich die Garbage-Collection-Leistung von Go bis zur Version 1.5 verbessert hat. Für relativ ausgereifte Garbage-Collection-Systeme (wie Java JVM und Javascript v8) ist jedoch noch ein langer Weg zur Optimierung erforderlich (aber). Ich glaube, die Zukunft muss wunderbar sein~).

Praktische Erfahrung

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 .

Das Problem der großen Speichernutzung des Go-Programms

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.

Das Problem der langen GC-Zeit

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~

Das Problem des Goroutine-Lecks

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.

Grundkenntnisse zu Golang-GC

30. April 2016, 20:02 Uhr | KOMMENTARE

In diesem Teil werden hauptsächlich einige Einführungskenntnisse zu Golang-GC vorgestellt.

Hintergrund von Golang GC

  • Golang ist eine Sprache, die auf Garbage Collection basiert, was ihr Designprinzip ist.
  • Als Sprache mit Garbage Collector wirkt sich die Effizienz der GC-Interaktion mit dem Programm auf die Betriebseffizienz des gesamten Programms aus.
  • Normalerweise wirkt sich die Speicherverwaltung des Programms selbst auf die Effizienz zwischen gc und dem Programm aus und verursacht sogar Leistungsengpässe.

Golang GC-bezogene Probleme

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.

Speicherverlust

wird aus der Sicht des Betriebssystems erklärt. Die Metapher ist, dass „der Speicherplatz (virtueller Speicherplatz), den das Betriebssystem allen Prozessen zur Verfügung stellen kann, durch einen bestimmten Prozess erschöpft wird.“ Wenn das Programm ausgeführt wird, öffnet es kontinuierlich dynamisch Speicherplatz, und dieser Speicherplatz wird nach Abschluss der Ausführung nicht rechtzeitig freigegeben. Nachdem eine Anwendung aufgrund von Entwurfsfehlern ein bestimmtes Speichersegment zugewiesen hat, verliert das Programm möglicherweise die Kontrolle über das Speichersegment, was zu einer Verschwendung von Speicherplatz führt.

Wenn 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.

    Es gibt viele damit zusammenhängende Probleme im Zusammenhang mit Speicherlecks, die hier nicht besprochen werden.
  • Gemeinsame GC-Modi

    Hier finden Sie Informationen zu spezifischen Vor- und Nachteilen. Hier finden Sie nur eine allgemeine Einführung.

    • Referenzzählung (Referenzzählung) Jedes Objekt verwaltet einen Referenzzähler. Wenn das auf das Objekt verweisende Objekt zerstört oder aktualisiert wird, wird der Referenzzähler des referenzierten Objekts automatisch um 1 verringert. Wenn das angewendete Objekt erstellt oder zugewiesen wird Bei anderen Objekten ist die Referenz +1, und wenn die Referenz 0 ist, wird sie recycelt. Die Idee ist einfach, aber die häufige Aktualisierung des Referenzzählers verringert die Leistung. Es gibt eine Schleife zum Referenzieren (wird von PHP und Python verwendet)
    • Markieren und Sweepen (Markieren und Sweepen) ist Golang. Es wird verwendet, um alle referenzierten Objekte aus der Stammvariablen zu durchlaufen, Löschvorgänge nach dem Markieren durchzuführen und nicht markierte Objekte zu recyceln. Nachteile: Der gesamte normal laufende Code wird bei jeder Speicherbereinigung angehalten , und die Reaktionsfähigkeit des Systems wird stark beeinträchtigt, verschiedene Mark&Swamp-Varianten (dreifarbige Markierungsmethode), wodurch Leistungsprobleme gemildert werden.
    • Generationensammlung (Generation) jvm nutzt die Idee des Generationenrecyclings. In objektorientierten Programmiersprachen ist der Lebenszyklus der meisten Objekte sehr kurz. 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ängeren Lebenszyklen gefördert bis ins hohe Alter (hier wird ein Klassifikationsgedanke verwendet, der auch ein Grundgedanke des ​​wissenschaftlichen Denkens ist).
    • Aus diesem Grund sind zwei verschiedene Garbage-Collection-Methoden entstanden, die Garbage-Collection der neuen Generation und die Garbage-Collection der alten Generation (zuerst klassifizieren und dann die richtige Medizin verschreiben), 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. So funktioniert GC in Golang normalerweise Eine Reihe von Objekten, die möglicherweise Referenzen zwischen diesen Objekten haben. Ein Tracing-Garbage Collector stoppt das laufende Programm zu einem bestimmten Zeitpunkt und scannt dann die Laufzeit. Es handelt sich bereits um eine Reihe von Objekten, bei denen es sich normalerweise um globale Variablen und verschiedene Objekte handelt die im Stapel vorhanden sind. gc markiert diese Objekte, markiert den Status dieser Objekte als erreichbar, ermittelt alle Referenzen von Objekten an anderen Orten, die von den aktuellen Objekten aus erreichbar sind, und markiert diese Objekte als erreichbare Objekte. Dieser Schritt wird als Markierungsphase bezeichnet ist,
    Markierungsphase

    Der Hauptzweck dieses Schritts besteht darin, die Statusinformationen dieser Objekte zu erhalten.

    Sobald alle diese Objekte gescannt wurden, ruft gc alle nicht erreichbaren Objekte (Objekte mit dem Status „nicht erreichbar“) ab und recycelt sie. Dieser Schritt wird als Sweep-Phase bezeichnet, die „Sweeping-Phase“ ist.

    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

    2014/6 1.3 Einführung der gleichzeitigen Reinigung (gleichzeitige Ausführung von Speicherbereinigung und Benutzerlogik?)

    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?

    Ich habe schon so viel gesagt, aber wie misst man die Sterneffizienz von GC und stellt fest, ob sie einen Einfluss auf die Ausführung des Programms hat? Die erste Möglichkeit besteht darin, die Umgebungsvariablen von Godebug festzulegen. Weitere Informationen finden Sie in diesem Artikel, der wirklich gut ist: Link, zum Beispiel: Führen Sie 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)
    • Tseq stellt die Zeit dar, die erforderlich ist, um die Goroutine des Benutzers zu stoppen und einige Vorbereitungsaktivitäten (normalerweise kleine) durchzuführen Benutzer-Goroutinen werden gestoppt, sodass die Verarbeitungsverzögerung erheblich beeinträchtigt werden kann. Tsweep ist die Zeit zum Löschen des Heaps. Das Löschen erfolgt normalerweise gleichzeitig mit der normalen Programmausführung, sodass die Verzögerung nicht kritisch ist. Die Granularität ist weiter unterteilt , und das spezifische Konzept ist noch unklar:
    • Bezogen auf Tmark: 1. Die Anzahl der aktiven Objekte im Heap während der Speicherbereinigung, 2. Die Gesamtmenge des von aktiven Objekten mit Zeigern belegten Speichers, 3. Die Anzahl der Zeiger in aktiven Objekten.
    Bezogen auf Tsweep: 1 Die Gesamtmenge des Heap-Speichers 2 Die Gesamtmenge des Mülls im Heap

      So führt man ein GC-Tuning durch (Gopher-Konferenz Danny)
    • Harte Parameter
    • Bei Algorithmusproblemen gibt es immer einige Parameter . Der GOGC-Parameter steuert hauptsächlich die Speichernutzung beim Start des nächsten GC.

    Zum Beispiel verwendet das aktuelle Programm 4 MB Speicher (hier sprechen wir von

    Heapspeicher

    ), was bedeutet, dass der aktuell erreichbare Speicher des Programms 4 MB beträgt, wenn der vom Programm belegte Speicher erreichbar ist*(1+ GOGC/100)= Bei 8M wird GC ausgelöst und startet zugehörige GC-Vorgänge.

    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 reduzieren

    Die sogenannte Reduzierung der Objektzuordnung bedeutet eigentlich, Objekte so weit wie möglich wiederzuverwenden. Zum Beispiel die folgenden zwei Funktionsdefinitionen:

    Die erste Funktion hat keine formalen Parameter und gibt bei jedem Aufruf ein []Byte-Objekt zurück gibt die Anzahl der gelesenen Bytes zurück.

    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!

Stellungnahme:
Dieser Artikel ist reproduziert unter:csdn.net. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen