gctrace и снижение давления
На ночном инциденте Миша включил журнал сборщика и вместе с Аней читал строку за строкой: живое росло от цикла к циклу. Сергей охладил пыл: кэш тоже растёт, пока не выйдет на плато.
GODEBUG=gctrace=1 печатает по строке на цикл GC — из неё видны давление на GC и подозрения на утечки; а главный способ «настроить» GC — убрать лишние аллокации.
Строка gctrace
gc 12 @0.183s 2%: 0.087+1.4+0.020 ms clock, 0.69+0/0.55/0.24+0.16 ms cpu, 58->59->29 MB, 60 MB goal, 8 P
12— номер цикла,@0.183s— момент от старта,2%— доля времени в GC.- Три времени до
ms clock— sweep termination, mark и mark termination: первое и третье — STW-паузы (0.087 и 0.020 мс), середина (1.4 мс) — конкурентная маркировка. - В
ms cpuу mark-фазы тройная разбивка: assist / фон / простой. 58->59->29 MB— куча в начале цикла, в конце и объём живых объектов.60 MB goal— целевой размер кучи от пейсера,8 P— число процессоров.- Пример выше — формат не самых свежих версий: в свежих (Go 1.27+) между
goalи числом P добавились поляMB stacksиMB globals. - Если строка заканчивается на
(forced)— сборку вызвали вручную черезruntime.GC.
Как читать на практике
Формат строки менялся между версиями, а читается одинаково: растущее от цикла к циклу третье число в ->->N MB — сигнал, а не доказательство утечки. Легитимный кэш тоже растёт, пока не выйдет на плато: проверяй стабилизацию и абсолютные значения (сравнение с лимитом), прежде чем объявлять утечку по паре циклов. Для точечных замеров есть runtime.ReadMemStats (вызов не бесплатен — на короткое время останавливает мир, не зови в горячем цикле) и runtime/metrics в свежих версиях — читается без остановки мира.
Как снизить давление
Сборщик настроен хорошо, и тюнинг GOGC — последний шаг, а не первый. Сначала убери лишние аллокации:
- Возвращай значение вместо указателя там, где структура маленькая и копия дёшевая.
- Не гоняй данные через
anyбез необходимости: бокс значения крупнее машинного слова — аллокация. - Выделяй слайсы с реалистичной capacity, чтобы append не пересоздавал массивы.
- Переиспользуй тяжёлые объекты через
sync.Pool. - Строй строки через
strings.Builder, а не конкатенацией в цикле.
Вы узнаете:
- чем
runtime/metricsлучшеReadMemStats - как отличить «живое растёт» от легитимного кэша
Проверьте себя
Живое в gctrace растёт от цикла к циклу. Это утечка?
Что первым делом при давлении на GC?
Что означает (forced) в конце строки gctrace?
правильных ответов
gctrace — по строке на цикл: растущее «живое» — сигнал к проверке (стабилизация и абсолюты), а не доказательство утечки. Снижение давления — сначала аллокации, GOGC — потом.
10 000 ₽ · 60 минут · онлайн