Garbage Collection in Go
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy. Lektion 23 von 107.
Im Gegensatz zu Sprachen wie C oder C++ verwaltet Go den Speicher automatisch durch Garbage Collection (GC). Du musst den Speicher nicht manuell freigeben, wenn du ihn nicht mehr benötigst. Die Go-Laufzeitumgebung verfolgt, welcher Speicher noch verwendet wird, und gibt den restlichen Speicher frei.
Wenn du Speicher mit new(), make() oder durch das Erstellen von Variablen zuweist, überwacht Go's Garbage Collector diese Zuweisungen. Sobald ein Wert von keinem Teil deines Programms mehr erreichbar ist, gibt der GC diesen Speicher automatisch frei.
func createPerson() *Person {
p := &Person{Name: "Alice", Age: 30}
return p // p entkommt auf den Heap, GC verwaltet es
}
func main() {
person := createPerson()
fmt.Println(person.Name)
// Wenn person nicht mehr verwendet wird, gibt GC den Speicher frei
}In diesem Beispiel wird die Person-Struktur auf dem Heap alloziert, weil sie von der Funktion zurückgegeben wird. Der Compiler von Go führt eine Escape-Analyse durch, um festzustellen, ob eine Variable auf dem Stack bleiben kann oder auf den Heap verschoben werden muss. Der Garbage Collector verwaltet nur den Heap-Speicher.
Diese automatische Speicherverwaltung bedeutet, dass du dich auf das Erstellen deiner Anwendung konzentrieren kannst, ohne dir Gedanken über Speicherlecks durch vergessene Freigaben machen zu müssen. Wenn du jedoch verstehst, dass GC existiert, kannst du effizienteren Code schreiben, indem du unnötige Speicherzuweisungen minimierst, wenn die Leistung wichtig ist.
Aufgabe
EinfachErstellen wir einen Sitzungsmanager, der zeigt, wie Go's Garbage Collector den Speicher für Objekte verwaltet, die auf den Heap ausgelagert werden. Du erstellst Sitzungen, die dynamisch alloziert und aus Funktionen zurückgegeben werden, sodass der GC ihren Lebenszyklus verwalten kann.
Du organisierst deinen Code über zwei Dateien:
session.go: Definiere eineSession-Struktur mit den FeldernID(String),Username(String) undData(ein String-Slice). Erstelle eineNewSession-Funktion, die eine ID und einen Benutzernamen entgegennimmt, eine neue Session auf dem Heap alloziert (indem sie einen Zeiger zurückgibt) und das Data-Slice als leer initialisiert. Füge außerdem eineAddData-Methode mit einem Zeiger-Empfänger hinzu, die einen String an das Data-Slice der Sitzung anhängt, sowie eineSummary-Methode, die einen formatierten String mit den Sitzungsdetails zurückgibt.main.go: Lies Sitzungsinformationen aus der Eingabe, erstelle mithilfe deiner Konstruktorfunktion eine Sitzung, füge einige Dateneinträge hinzu und zeige die Sitzungszusammenfassung an. Da die Sitzung innerhalb einer Funktion erstellt und als Zeiger zurückgegeben wird, wird sie auf den Heap ausgelagert, wo der Garbage Collector sie verwalten wird.
Die folgenden Eingaben werden bereitgestellt:
- Zeile 1: Sitzungs-ID
- Zeile 2: Benutzername
- Zeile 3: Erster hinzuzufügender Dateneintrag
- Zeile 4: Zweiter hinzuzufügender Dateneintrag
Deine Summary-Methode sollte einen String in diesem Format zurückgeben:
Session [ID] for user [Username]
Data entries: [count]
- [entry1]
- [entry2]Wenn beispielsweise sess-001, alice, login und view_dashboard angegeben werden, sollte deine Ausgabe folgendermaßen aussehen:
Session sess-001 for user alice
Data entries: 2
- login
- view_dashboardDie zentrale Erkenntnis hierbei ist, dass deine NewSession-Funktion eine Session erstellt und einen Zeiger darauf zurückgibt. Dadurch wird die Session auf den Heap „ausgelagert“, anstatt auf dem Stack zu verbleiben, und kann vom Garbage Collector bereinigt werden, sobald keine Referenzen mehr auf sie vorhanden sind. Dein Code muss diesen Speicher nicht manuell freigeben: Go erledigt das automatisch.
Probier es selbst
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Sitzungs-ID lesen
sessionID, _ := reader.ReadString('\n')
sessionID = sessionID[:len(sessionID)-1]
// Benutzernamen lesen
username, _ := reader.ReadString('\n')
username = username[:len(username)-1]
// Ersten Dateneintrag lesen
data1, _ := reader.ReadString('\n')
data1 = data1[:len(data1)-1]
// Zweiten Dateneintrag lesen
data2, _ := reader.ReadString('\n')
data2 = data2[:len(data2)-1]
// TODO: Erstelle eine neue Session mit der NewSession-Funktion
// Die Session wird auf den Heap escapen, da wir einen Zeiger zurückgeben
// TODO: Füge die Dateneinträge mit der AddData-Methode zur Session hinzu
// TODO: Gib die Session-Zusammenfassung mit der Summary-Methode aus
fmt.Println("")
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP in Go
Externe DateienGo-Workspace & ModulePackages & ImportsExportierte vs. nicht exportierte NamenEinführung in OOP mit GoStructs als KlassenMethoden für Structs definierenPointer- vs. Value-ReceiverStruct-InitialisierungKonstruktorfunktionenRückblick – Einfacher Taschenrechner4Schnittstellen
Einführung in SchnittstellenImplizite ImplementierungSchnittstelle als VertragLeere Schnittstelle (any)TypzusicherungTypwechselZusammensetzung von SchnittstellenStringer- und Error-SchnittstellenRückblick – Formenrechner7Kapselung
Exportierte vs. nicht exportierte FelderKapselung auf PaketebeneGetter- und Setter-MethodenInformationsverbergung in GoRückblick – Studierendendatensätze10Generics (Go 1.18+)
Einführung in GenericsTypparameterTypbeschränkungenGenerische StrukturenWorkaround für generische MethodenZusammenfassung – Generische Sammlung2Typen & Structs im Detail
Grundlegende & zusammengesetzte TypenBenutzerdefinierte TypdefinitionenStruct-TagsAnonyme StructsVerschachtelte StructsNullwerte & StandardwerteRückblick – Kontaktbuch5Komposition statt Vererbung
Warum Go keine Vererbung hatGrundlagen der Struct-EinbettungMethoden-PromotionMehrere Structs einbettenEinbettung vs. AggregationVerbergen eingebetteter MethodenRückblick – Mitarbeiterhierarchie8Fehlerbehandlung & OOP
Das Error-InterfaceBenutzerdefinierte FehlertypenError-Wrapping (fmt.Errorf)Sentinel-Fehlererrors.Is() und errors.As()Panic, Defer und RecoverRückblick – Dateiparser3Zeiger & Speicher
Grundlagen von Zeigern in GoZeiger auf StructsÜbergabe per Wert vs. ReferenzDie Funktion new()Garbage Collection in GoRückblick – Verkettete Liste erstellen6Polymorphismus in Go
Polymorphismus über InterfacesDuck-Typing in GoRegeln zur Interface-ErfüllungPolymorphe SammlungenDependency InjectionZusammenfassung – Zahlungsprozessor9Konkurrenz & OOP
Grundlagen der GoroutinesChannels & KommunikationGepufferte vs. ungepufferte ChannelsSelect-Anweisungsync.Mutex & sync.RWMutexsync.WaitGroupThread-sicheres Struct-DesignRückblick – Worker PoolÜbe selbstständig: Online-Go-Compiler