Урок по треку Go 8 мин

Память, сборщик мусора и pprof

Стек и куча, escape analysis, трёхцветный GC, GOGC и GOMEMLIMIT, профилирование через pprof.

На middle и senior собесах спрашивают, где Go размещает переменные, как работает GC и как найти, что тормозит или течёт.

Стек и куча

У каждой горутины свой стек, он растёт копированием (старый стек копируется в вдвое больший). Выделение на стеке почти бесплатно и не нагружает GC. Куча общая, её чистит сборщик.

Куда попадёт переменная, решает компилятор, а не new или &. Это escape analysis: если значение может пережить функцию или компилятор не может это доказать, оно «убегает» в кучу.

func newUser() *User {
    u := User{Name: "a"}
    return &u // u убегает в кучу: указатель живёт после возврата
}

Типичные причины побега: возврат указателя, сохранение в глобальную переменную или поле, передача в интерфейс (часто, например fmt.Println(x)), замыкание, захватывающее переменную, слайс неизвестного на этапе компиляции размера. Посмотреть: go build -gcflags=-m.

Сборщик мусора

GC в Go конкурентный, неперемещающий, трёхцветный mark-and-sweep без поколений.

  1. Короткая пауза stop-the-world: включается write barrier.
  2. Конкурентная маркировка: объекты белые (не проверены), серые (найдены, но поля не просмотрены), чёрные (просмотрены). Начинаем с корней (стеки, глобальные переменные), пока серые не кончатся.
  3. Write barrier не даёт программе спрятать белый объект за чёрным во время маркировки.
  4. Короткая пауза: завершение маркировки.
  5. Sweep: белые объекты освобождаются лениво, по мере новых аллокаций.

Паузы обычно меньше миллисекунды. Цена это процессорное время: горутины, которые много аллоцируют, привлекаются помогать маркировке (mark assist).

GOGC и GOMEMLIMIT

  • GOGC=100 (по умолчанию): следующий цикл GC начнётся, когда живая куча вырастет на 100% от размера после прошлой сборки. Больше значение: реже GC, больше памяти.
  • GOMEMLIMIT (Go 1.19+): мягкий лимит памяти. При приближении к нему GC работает чаще. В контейнере ставят около 80-90% лимита, иначе процесс может убить OOM killer раньше, чем GC проснётся.
  • GOGC=off плюс GOMEMLIMIT: собирать только у лимита.

В Go 1.25 появился экспериментальный сборщик Green Tea (GOEXPERIMENT=greenteagc), который маркирует память блоками, лучше используя кеш процессора.

Как меньше нагружать GC

  • Предвыделять слайсы и мапы: make([]T, 0, n).
  • Переиспользовать буферы через sync.Pool.
  • Меньше указателей в больших структурах: GC сканирует только память с указателями.
  • strings.Builder вместо +=.

Утечки в Go

Сборщик есть, а утечки бывают: зависшие горутины, растущие без ограничения мапы-кеши (мапа не уменьшает свою таблицу после удаления ключей), маленький срез большого массива, забытые тикеры в старых версиях, незакрытые resp.Body.

pprof

import _ "net/http/pprof" // регистрирует /debug/pprof/ на DefaultServeMux
go http.ListenAndServe("localhost:6060", nil)
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30   # CPU
go tool pprof http://localhost:6060/debug/pprof/heap                 # память
curl http://localhost:6060/debug/pprof/goroutine?debug=2             # стеки горутин
  • profile (CPU): где тратится время, сэмплирование 100 раз в секунду.
  • heap: inuse_space (что живо сейчас, для утечек) и alloc_space (сколько всего выделено, для давления на GC).
  • goroutine: стеки всех горутин, ищем утечки.
  • block и mutex: где ждут на синхронизации, включаются runtime.SetBlockProfileRate и SetMutexProfileFraction.
  • trace (go tool trace): временная шкала планировщика, GC, блокировок.

В интерфейсе pprof: top, list Func, web или -http=:8080 с флеймграфом. Не открывайте pprof наружу: только внутренний порт.

Из тестов: go test -bench . -cpuprofile cpu.out -memprofile mem.out.

Что спросят на собесе

  • Где размещаются переменные и что такое escape analysis.
  • Как работает трёхцветный GC и зачем write barrier.
  • Что делают GOGC и GOMEMLIMIT, что выставить в контейнере.
  • Как найти утечку памяти или горутин.
  • Какие профили есть в pprof и чем inuse отличается от alloc.

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.