Irbisa · Шпаргалка
Рантайм, планировщик и GC
G-M-P, вытеснение, escape-анализ, GOGC, GOMEMLIMIT, pprof
Кто освобождает память в Go и может ли Go-программа всё-таки течь?
Как работает планировщик Go: что такое G, M и P и зачем нужен
P?С
GOMAXPROCS(1)эта программа на Go 1.13 зависала навсегда, а сегодня печатаетdone. Планировщик Go кооперативный или вытесняющий и что изменилось?func main() { runtime.GOMAXPROCS(1) go func() { for { // no function calls, no allocations } }() time.Sleep(10 * time.Millisecond) fmt.Println("done")}Что делает
sysmonв рантайме Go и почему он работает безP?У этой рекурсии нет базового случая. Что напечатает программа, до какого размера вырос стек горутины и почему
recoverеё не спасает?func depth(n int) int { return depth(n+1) + 1} func main() { defer func() { if r := recover(); r != nil { fmt.Println("recovered:", r) } }() fmt.Println(depth(0))}Что задаёт
GOMAXPROCSи как с ним взаимодействует лимит CPU контейнера до и после Go 1.25?Как найти в Go-сервисе функцию, которая жжёт CPU, с помощью
pprof: как его включить, снять профиль, прочитатьtopи flame graph и сколько это стоит?import ( "log" "net/http" _ "net/http/pprof" // registers /debug/pprof/* on http.DefaultServeMux)…Что такое escape analysis? Какие из этих функций кладут значение в кучу и как это проверить?
type Point struct{ X, Y int } var global *Point func local() int { p := &Point{1, 2} return p.X} func returned() *Point { p := Point{1, 2} return &p} func stored() { global = &Point{3, 4} }func printed(n int) { fmt.Println(n) }Как работает сборщик мусора в Go: трёхцветная разметка, барьер записи и когда останавливается мир?
Что задаёт
GOGC, почему сервис с живой кучей 100 МБ занимает около 200 МБ и что будет, если значение поднять?Что печатают
GODEBUG=gctrace=1иschedtrace=1000и как читать строкуgctrace?$ GODEBUG=gctrace=1,schedtrace=1000 ./service 2> runtime.log gc 42 @12.513s 4%: 0.031+3.1+0.052 ms clock, 0.25+0.40/6.1/9.0+0.42 ms cpu, 118->121->62 MB, 124 MB goal, 0 MB stacks, 0 MB globals, 8 P SCHED 13004ms: gomaxprocs=8 idleprocs=0 threads=14 spinningthreads=0 needspinning=0 idlethreads=4 runqueue=37 [12 9 30 4 18 22 7 15]Найдите баг: сервис хранит лишь 256-байтовое превью каждой загрузки, но живая куча растёт на мегабайты с каждым файлом.
var mu sync.Mutexvar previews = map[string][]byte{} // first bytes, shown in the file list func saveUpload(id string, r io.Reader) error { data, err := io.ReadAll(r) // uploads are 1–20 MB if err != nil { return err } if err := store(id, data); err != nil { return err } mu.Lock() previews[id] = data[:min(len(data), 256)] mu.Unlock() return nil}Что происходит с потоком ОС, когда горутина блокируется на канале, на чтении из сокета и на чтении файла или вызове
cgo?// Regular-file reads and cgo calls each hold an OS thread while they run:// bound them, or one burst leaves you with thousands of threads.type diskPool struct{ sem chan struct{} } func newDiskPool(n int) *diskPool { return &diskPool{sem: make(chan struct{}, n)} }…Как компилятор Go решает, что встраивать (inline), и почему встраивание может изменить, уходит ли значение в кучу?
type Point struct{ X, Y int } // -m: can inline NewPointfunc NewPoint(x, y int) *Point { return &Point{X: x, Y: y} } //go:noinline…Как устроен аллокатор памяти Go (классы размеров,
mcache,mcentral,mheap) и как он ограничивает фрагментацию, не перемещая объекты?Сервис тратит треть CPU в
runtime.mallocgcи воркерах GC. Как сократить аллокации на горячем пути и как доказать каждое исправление?type Row struct { Name string Count int64} var bufs = sync.Pool{New: func() any { return new(bytes.Buffer) }}…Найдите баг: под нагрузкой некоторые ответы API содержат JSON другого запроса.
var bufPool = sync.Pool{New: func() any { return new(bytes.Buffer) }} func encode(v any) ([]byte, error) { buf := bufPool.Get().(*bytes.Buffer) buf.Reset() defer bufPool.Put(buf) if err := json.NewEncoder(buf).Encode(v); err != nil { return nil, err } return buf.Bytes(), nil}Профиль кучи ровно держится на 300 МБ, но RSS контейнера стоит на 1,4 ГБ, и
runtime.GC()его не двигает. Где память?// metrics.Read does not stop the world; runtime.ReadMemStats does, briefly.var samples = []metrics.Sample{ {Name: "/memory/classes/total:bytes"}, // everything the runtime has mapped {Name: "/memory/classes/heap/released:bytes"}, // returned to the OS: not in RSS {Name: "/memory/classes/heap/objects:bytes"}, // live objects plus unswept garbage {Name: "/memory/classes/heap/free:bytes"}, // free pages the process still holds…Как найти утечку памяти в Go-сервисе и когда в профиле кучи смотреть
inuse_space, а когдаalloc_space?# live heap now, by allocation sitego tool pprof -inuse_space 'http://127.0.0.1:6060/debug/pprof/heap?gc=1' # two snapshots ten minutes apart, then show only what grewcurl -so before.pb.gz 'http://127.0.0.1:6060/debug/pprof/heap?gc=1'sleep 600…Под на 2 ГиБ с живой кучей около 700 МБ получает OOM-kill на всплесках трафика, а старый ранбук предлагает балласт на 1 ГиБ. Что настроить вместо него и что эта настройка считает?
// Read the cgroup v2 limit and keep headroom for what GOMEMLIMIT does not count.func setMemoryLimit(fraction float64) { b, err := os.ReadFile("/sys/fs/cgroup/memory.max") // "max" when unlimited if err != nil { return }…Под нагрузкой пропускная способность обваливается, а
gctraceпоказывает циклы подряд и assist больше фоновой разметки. Что такое mark assist и как это исправить?// Is the collector being paid for by request goroutines?// Counters are cumulative since start: read twice and compare the deltas.// "dedicated" is the dedicated background marking workers.func gcShares() (assist, background float64) { s := []metrics.Sample{ {Name: "/cpu/classes/gc/mark/assist:cpu-seconds"},…Архитектор отвергает Go для 40-гигабайтного кэша в памяти: «GC обязан обойти 40 ГБ с остановленным миром». Что на самом деле останавливается и что действительно растёт с кучей?
// wrong: ten million pointers the marker follows on every cycletype slowCache struct{ m map[string]*Entry } // right: no pointers inside, so the GC does not scan the data at alltype cache struct { index map[uint64]extent // hashed key -> position; no pointers in key or value…Что изменилось в сборщике мусора в Go 1.26 с Green Tea и что команде проверить при обновлении?
p99 составляет 800 мс, а CPU-профиль почти ничего из этого не объясняет. Какие профили показывают, чего ждали запросы, и когда нужен
go tool trace?var fr = trace.NewFlightRecorder(trace.FlightRecorderConfig{ MinAge: 10 * time.Second, // keep at least the last 10 s MaxBytes: 64 << 20,}) func main() {…Чего стоит вызов
cgoпланировщику и почему C нельзя сохранять Go-указатель, тем болееuintptr, после возврата из вызова?/*#include <stddef.h>static void *kept;static void register_buffer(void *p, size_t n) { kept = p; }static void unregister_buffer(void) { kept = NULL; }*/…