Weak Pointer in Go 1.24 verstehen
Lukas Schneider
DevOps Engineer · Leapcell

In Go bezieht sich ein Weak Pointer auf eine Referenz, die den Garbage Collector (GC) nicht daran hindert, das Zielobjekt freizugeben.
Wenn ein Objekt nur von Weak Pointern referenziert wird und keine Strong References hat, behandelt der GC es weiterhin als nicht erreichbar und gibt es frei. Danach werden alle Weak Pointer darauf automatisch zu nil
.
Kurz gesagt, ein Weak Pointer erhöht nicht die Referenzanzahl eines Objekts. Wenn ein Objekt nur von Weak Pointern referenziert wird, kann der Garbage Collector es freigeben. Daher sollten Sie vor dem Versuch, den Wert eines Weak Pointers zu verwenden, prüfen, ob er nil
ist.
Das weak
Package in Go 1.24
Go 1.24 führt das weak
Package ein, das eine prägnante API zum Erstellen und Verwenden von Weak Pointern bietet.
import "weak" type MyStruct struct { Data string } func main() { obj := &MyStruct{Data: "example"} wp := weak.Make(obj) // Weak Pointer erstellen val := wp.Value() // Strong Reference oder nil erhalten if val != nil { fmt.Println(val.Data) } else { fmt.Println("object has been garbage collected") } }
Im obigen Beispiel erstellt weak.Make(obj)
einen Weak Pointer auf obj
.
Beim Aufruf von wp.Value()
wird eine Strong Reference zurückgegeben, wenn das Objekt noch aktiv ist; andernfalls wird nil
zurückgegeben.
Testen von Weak Pointern
import ( "fmt" "runtime" "weak" ) type MyStruct struct { Data string } func main() { obj := &MyStruct{Data: "test"} wp := weak.Make(obj) obj = nil // Strong Reference entfernen runtime.GC() if wp.Value() == nil { fmt.Println("object has been garbage collected") } else { fmt.Println("object is still alive") } }
Indem Sie die Strong Reference obj
auf nil
setzen und explizit GC auslösen,
können Sie beobachten, dass der Weak Pointer nil
zurückgibt, sobald das Objekt eingesammelt wurde.
Unterschiede zwischen Weak Pointern und Strong References
Auswirkung auf Garbage Collection (GC):
- Strong References halten Objekte am Leben.
- Weak References halten Objekte nicht am Leben.
Nil-Werte:
- Eine Strong Reference ist
nil
, wenn sie explizit auf nichts zeigt. - Eine Weak Reference ist normalerweise
nil
, weil das Zielobjekt bereits eingesammelt wurde oder noch nicht zugewiesen wurde.
Zugriffsmethode:
- Eine Strong Reference kann das Objekt direkt dereferenzieren.
- Ein Weak Reference erfordert den Aufruf der
Value()
-Methode, bevor auf das Objekt zugegriffen wird.
Beispiel 1: Verwenden von Weak Pointern für temporäres Caching
Ein typisches Szenario für Weak Pointer ist das Speichern von Einträgen in einem Cache, ohne zu verhindern, dass sie vom GC eingesammelt werden.
package main import ( "fmt" "runtime" "sync" "weak" ) type User struct { Name string } var cache sync.Map // map[int]weak.Pointer[*User] func GetUser(id int) *User { // ① Zuerst versuchen, aus dem Cache zu holen if wp, ok := cache.Load(id); ok { if u := wp.(weak.Pointer[User]).Value(); u != nil { fmt.Println("cache hit") return u } } // ② Tatsächlich laden (hier nur konstruieren) u := &User{Name: fmt.Sprintf("user-%d", id)} cache.Store(id, weak.Make(u)) fmt.Println("load from DB") return u } func main() { u := GetUser(1) // von DB laden fmt.Println(u.Name) runtime.GC() // Selbst wenn GC sofort ausgeführt wird, hält `main` eine Strong Ref, sodass User überlebt u = nil // Letzte Strong Reference freigeben runtime.GC() // GC auslösen, User kann eingesammelt werden _ = GetUser(1) // Wenn eingesammelt, wird er erneut von DB geladen }
In dieser Cache-Implementierung werden Einträge als Weak Pointer gespeichert. Wenn das Objekt keine anderen Strong References hat, kann der GC es freigeben; beim nächsten Aufruf von GetUser
werden die Daten neu geladen.
Das Ausführen des obigen Codes erzeugt die folgende Ausgabe:
$ go run cache.go load from DB user-1 load from DB
Warum Weak Pointer verwenden?
Zu den häufigsten Szenarien gehören:
- Caching: Speichern Sie Objekte, ohne sie zu zwingen, im Speicher zu bleiben; wenn sie nicht anderweitig verwendet werden, können sie eingesammelt werden.
- Observer Pattern: Behalten Sie Referenzen auf Observer, ohne zu verhindern, dass sie eingesammelt werden.
- Kanonisierung: Stellen Sie sicher, dass nur eine Instanz desselben Objekts vorhanden ist, während nicht verwendete Instanzen eingesammelt werden können.
- Abhängigkeitsgraphen: Verhindern Sie Referenzzyklen in Strukturen wie Bäumen oder Graphen.
Hinweise zur Verwendung von Weak Pointern
- Immer auf nil prüfen: Objekte können in jedem GC-Zyklus eingesammelt werden; das Ergebnis von
Value()
darf nicht zwischengespeichert werden. - Vermeiden Sie zirkuläre Abhängigkeiten: Lassen Sie Objekte, auf die von Weak Pointern verwiesen wird, keine Strong References zurück zum Container halten, da sich sonst immer noch Zyklen bilden können.
- Performance-Kompromisse: Der Zugriff auf einen Weak Pointer erfordert einen zusätzlichen Aufruf, und das häufige Neuladen von Objekten, die auf
nil
zurückfallen, kann zu Turbulenzen führen.
Beispiel 2: Normale Verwendung von Strong Pointern
package main import ( "fmt" "runtime" ) type Session struct { ID string } func main() { s := new(Session) // äquivalent zu &Session{} s.ID = "abc123" fmt.Println("strong ref alive:", s.ID) s = nil // Letzte Strong Reference entfernen runtime.GC() // Versuch, GC auszulösen (nur zur Demo, tatsächliches Timing hängt von der Laufzeit ab) fmt.Println("done") }
Hier ist s
ein Strong Pointer. Solange er erreichbar bleibt, wird das Session
-Objekt niemals vom GC eingesammelt.
Wann wird ein Objekt, auf das ein Strong Pointer zeigt, eingesammelt?
-
Erreichbarkeitsanalyse: Go verwendet einen Mark-and-Sweep-GC. Zu Beginn eines GC-Zyklus durchläuft die Laufzeit alle Strong References von Root-Objekten (Stack, globale Variablen, aktuelle Register usw.).
- Objekte, die über Strong References erreichbar sind, gelten als erreichbar und bleiben aktiv.
- Alle anderen werden als nicht erreichbar markiert und während der Sweep-Phase freigegeben.
-
Keine Referenzzählung: Go prüft nur, ob ein Objekt von Roots aus erreichbar ist. Die Anzahl der Referenzen oder ob Werte gleich sind, hat keinen Einfluss auf die Sammlung.
-
Unsichere Zeitplanung: GC-Zyklen werden automatisch vom Scheduler ausgelöst. Entwickler können
runtime.GC()
als Hinweis aufrufen, dies garantiert jedoch keine sofortige Sammlung. -
Variablen selbst können eingesammelt werden: Wenn sich eine Strong-Pointer-Variable
s
auf dem Heap befindet und die Struktur, die sie enthält, nicht mehr erreichbar ist, kanns
selbst eingesammelt werden. Stack-Variablen werden freigegeben, wenn die Funktion zurückkehrt.
Zusammenfassung
Ein Strong Pointer garantiert, dass das Objekt, auf das er zeigt, über alle GC-Zyklen hinweg im „Live-Set“ verbleibt. Sobald die letzte Strong Reference unterbrochen ist, wird das Objekt während des nächsten GC-Zyklus automatisch freigegeben.
Wir sind Leapcell, Ihre erste Wahl für das Hosting von Go-Projekten.
Leapcell ist die Next-Gen Serverless-Plattform für Webhosting, asynchrone Aufgaben und Redis:
Multi-Language Support
- Entwickeln Sie mit Node.js, Python, Go oder Rust.
Stellen Sie unbegrenzt Projekte kostenlos bereit
- Zahlen Sie nur für die Nutzung - keine Anfragen, keine Gebühren.
Unschlagbare Kosteneffizienz
- Pay-as-you-go ohne Leerlaufgebühren.
- Beispiel: 25 US-Dollar unterstützt 6,94 Mio. Anfragen bei einer durchschnittlichen Antwortzeit von 60 ms.
Optimierte Entwicklererfahrung
- Intuitive Benutzeroberfläche für mühelose Einrichtung.
- Vollautomatische CI/CD-Pipelines und GitOps-Integration.
- Echtzeitmetriken und Protokollierung für umsetzbare Erkenntnisse.
Mühelose Skalierbarkeit und hohe Leistung
- Automatische Skalierung zur einfachen Bewältigung hoher Parallelität.
- Kein Betriebsaufwand - konzentrieren Sie sich einfach auf das Bauen.
Erfahren Sie mehr in der Dokumentation!
Folgen Sie uns auf X: @LeapcellHQ