Kompilator Go stosuje mechanizm escape analysis (analiza ucieczki wskaźników): wszystkie wartości domyślnie są umieszczane na stosie, ale jeśli zmienna "opuszcza" zasięg (na przykład, gdy zwracany jest wskaźnik do lokalnej zmiennej), automatycznie umieszczana jest na stercie (heap).
Jest to ważne dla wydajności:
Kompilator stara się określić, czy obiekt można umieścić na stosie, ale jeśli niejawnie “przekracza” granice funkcji — ląduje na stercie.
Przykład z ucieczką:
func NewPoint() *int { a := 42 return &a // ucieczka do heap! }
Przykład bez ucieczki:
func Sum(a, b int) int { c := a + b return c // na stosie }
Aby uzyskać szczegółowe informacje dotyczące diagnozy kompilatora, użyj go build -gcflags='-m', aby zobaczyć szczegóły:
go build -gcflags='-m' main.go
"Czy obiekt przejdzie do sterty, jeśli zwrócę z funkcji tylko jego wartość — nie wskaźnik?"
Często uważa się, że wszystkie zwracane wartości "przechodzą do sterty". W rzeczywistości, jeśli zwracana jest wartość (nie wskaźnik), zmienna może pozostać na stosie.
Przykład:
func F() int { x := 10 return x // alokacja stosu, nie przechodzi do heap }
Historia
Przy masowym tworzeniu struktur za pomocą funkcji fabrycznej, zwracającej wskaźniki do lokalnych zmiennych, gwałtownie wzrosło zużycie pamięci i obciążenie GC. Okazało się, że wszystkie obiekty trafiały do heap z powodu zwracania wskaźnika do lokalnej zmiennej, chociaż można było tego uniknąć, zwracając wartość.
Historia
W mikrousługach, z powodu przekazywania wskaźników między gorutynami i zwracania ich z różnych funkcji, z stosu “wyciekało” wiele obiektów do heap, co spowalniało działanie i powodowało częste przerwy GC.
Historia
Programista przypadkowo owinięty statyczny tablicę w slice wewnątrz funkcji, zwracał wskaźnik do slice — i podczas testu obciążeniowego zaczął łapać wycieki pamięci. Diagnoza wykazała: zmienna nie "wyłamała się" poza granice funkcji, ale Go błędnie zdecydował się umieścić ją w heap z powodu pośredniego użycia.