Speicher und Garbage Collector in Go: Wie der GC Ressourcen verwaltet

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Speicher und Garbage Collector in Go: Wie man mit dem GC nicht verrückt wird

\n

Haben Sie sich jemals gefragt, wohin der Speicher in Ihrer Go-Anwendung verschwindet? Besonders wenn sie anfängt, Gigabytes zu fressen, und Sie nur JSON parsen? Kommt Ihnen bekannt vor? Willkommen in der Welt des Garbage Collectors (GC) in Go – dem mysteriösen Mechanismus, der entweder Ihr Projekt rettet oder es in eine lahme Plattform verwandelt. Heute schauen wir uns an, wie Speicher in Go funktioniert, wie der GC entscheidet, wann aufgeräumt werden muss, und wie Sie nicht in die "STW-Wartefalle" tappen.

\n

Wenn Sie seit mindestens ein paar Monaten in Go programmieren, sind Sie dem schon begegnet: plötzliche Pausen, steigender Speicherverbrauch, rätselhafte gc pause in Profilen. Werfen wir einen Blick unter die Haube und verstehen, wie man Speicher in Go bewusst verwaltet.

\n\n

Wie Speicher in Go organisiert ist: Heap und Stack

\n

Bevor wir über den Garbage Collector sprechen, lassen Sie uns verstehen, wo die Daten in Go überhaupt leben. Anders als in Sprachen wie C, wo Sie selbst für malloc/free verantwortlich sind, verwendet Go automatische Speicherverwaltung. Aber das bedeutet nicht, dass alles in einen Topf geworfen wird.

\n

In Go gibt es zwei Hauptspeicherbereiche:

\n
    \n
  • Stack – schneller, automatisch verwalteter Speicher für lokale Variablen. Jede Goroutine hat ihren eigenen Stack (beginnt mit 2 KB und wächst dynamisch).
  • \n
  • Heap – langsamerer, aber flexibler Speicher für Daten, die einen Funktionsaufruf "überleben".
  • \n
\n

Die Hauptfrage: Was landet im Heap und was bleibt auf dem Stack? Dafür ist die Escape Analysis zuständig – ein intelligenter Mechanismus des Go-Compilers. Er analysiert, ob eine Variable den Gültigkeitsbereich der Funktion "verlässt". Wenn ja – geht sie in den Heap. Wenn nicht – bleibt sie auf dem Stack, und der GC erfährt nichts von ihr.

\n

Beispiel:

\n
package main\n\nfunc createPointer() *int {\n    x := 42\n    return &x // x "entkommt" – landet im Heap\n}\n\nfunc noEscape() int {\n    x := 42\n    return x // x bleibt auf dem Stack\n}\n
\n

Im ersten Fall sieht der Compiler, dass der Zeiger auf x nach außen zurückgegeben wird, und platziert die Variable im Heap. Im zweiten Fall ist alles einfach und schnell. Das ist Speicherverwaltung in Go auf Mikroebene.

\n\n

Der Garbage Collector in Go: Wie er funktioniert

\n

Kommen wir zum Hauptdarsteller – dem Garbage Collector (GC). In Go wird ein paralleler, dreifarbiger, nicht-inkrementeller Collector mit dem "Mark-and-Sweep"-Algorithmus verwendet. Klingt beängstigend? Eigentlich ist alles logisch.

\n

Der GC in Go durchläuft drei Phasen:

\n
    \n
  1. Mark Setup (STW) – eine kurze Pause zum Aktivieren des Write Barriers. Dauert normalerweise 10-30 Mikrosekunden.
  2. \n
  3. Marking (Concurrent) – die Hauptarbeit: Der GC durchläuft den Objektgraphen und markiert lebende Objekte. Wird parallel zu Ihrem Code ausgeführt.
  4. \n
  5. Sweep (Concurrent + STW) – Bereinigung toter Objekte. Wird teilweise parallel erledigt, aber die finale Synchronisation kann eine kurze Pause verursachen.
  6. \n
\n

Eine Schlüsseleigenschaft des GC in Go ist, dass er keine Defragmentierung durchführt. Das bedeutet, dass der Speicher nach der Bereinigung fragmentiert sein kann, aber Go verwendet Bitmaps zur Verfolgung freier Blöcke, was die Speicherzuweisung schnell macht.

\n

Ein Beispiel der GC-Arbeit sieht man in den Logs:

\n
GODEBUG=gctrace=1 go run main.go\n\ngc 1 @0.013s 4%: 0.031+0.42+0.017 ms clock, 0.25+0.10/0.42/0.0+0.13 ms cpu, 4->4->2 MB, 5 MB goal, 8 P\n
\n

Hier: 0.031+0.42+0.017 ms – das ist STW + Mark + STW. 4->4->2 MB – Heap-Größe vor, nach dem Mark und nach dem Sweep.

\n\n

Warum der GC bremst: STW und Pausen

\n

Der Hauptfeind der Leistung ist STW (Stop The World). Obwohl Go versucht, Pausen zu minimieren, treten sie dennoch auf. Besonders wenn:

\n
    \n
  • Sie viele Zeiger haben (der GC muss alle Objekte durchlaufen)
  • \n
  • Der Heap groß ist (mehr Objekte – längeres Marking)
  • \n
  • Sie viel Müll erzeugen (hohe Allokationsrate)
  • \n

Blogs

Buchempfehlungen