Cборщик мусора Green Tea в Go: что это и как работает

Коротко: в Go 1.25 появился экспериментальный сборщик мусора Green Tea. Его цель – уменьшить CPU-стоимость маркировки за счет лучшей локальности памяти и лучшей масштабируемости на многоядерных CPU. Типичные выигрыши по времени GC составляют около 10%, а на некоторых нагрузках – до 40%. В 1.26 его планируют сделать значением по умолчанию, с возможностью отключить. go.dev
🔥 https://t.me/+RAiQoS5k4Bg4NGYy – огромное количество уроков, библиотек и примеров с кодом в канале для Go разработчиков.
Зачем менять GC в Go
В классическом Go-GC основная цена – это фаза mark: на нее уходит ~90% времени GC, sweep – около 10%. При маркировке рантайм «затапливает» граф объектов, переходя по указателям. На реальном «железе» это приводит к множеству разрозненных чтений по памяти – кэш-промахи, остановки из-за ожидания данных и узкое место общей очереди заданий при параллельной работе на многих ядрах. go.dev
Главная идея Green Tea
Ключевой принцип: работать страницами памяти, а не отдельными объектами.

Вместо произвольного «скакания» по графу объектов Green Tea агрегирует работу по страницам хипа и проходит их длинными линейными сканами слева направо. Это резко повышает предсказуемость обращений к памяти, дружит с аппаратными префетчерами и разгружает общие очереди задач в многопоточном режиме. На больших хипах число «проходов» по памяти становится меньше и длиннее – именно в этом и «магия» Green Tea. go.dev
Здесь можно посмотреть видео про устройство green tea подробнее: https://www.youtube.com/watch?v=he5PfBfte2c
Аппаратные ускорения
Green Tea использует векторные инструкции современных x86-процессоров, чтобы обрабатывать метаданные целой страницы за несколько прямолинейных операций. Поддержка широких регистров (например, AVX-512) и продвинутых побитовых инструкций позволяет ускорить внутренний цикл сканирования. Эти векторные улучшения раскатывают постепенно и, по данным Go-команды, дадут дополнительное снижение CPU-стоимости GC примерно на 10%. go.dev

Что дают числа
- В типичных бенчмарках команда Go видит снижение времени GC на 10%, в ряде случаев – до 40%. Если приложение тратило 10% CPU на GC, то общий выигрыш может составить 1-4% CPU. Похожие эффекты наблюдают и в проде в Google. go.dev
- В релиз-ноутах 1.25 это зафиксировано как официальная оценка: «10-40% уменьшения накладных расходов GC» для программ, активно использующих GC. go.dev
Важно: не все нагрузки выигрывают. Если структура хипа такая, что на странице для сканирования часто оказывается ровно один объект, агрегирование только вредит. В реализации есть «спецкейс» для таких страниц – он сглаживает регрессии, но полностью их не исключает. Практика показала, что уже при заполнении страницы работой на ~2% Green Tea может обгонять классический обход графа. go.dev
Как включить и как откатить
Green Tea в Go 1.25 – эксперимент, его нужно явно включать при сборке:
# Обычная сборка
go build ./...
# С Green Tea GC
GOEXPERIMENT=greenteagc go build ./...
# Сборка без Green Tea, если вдруг он станет дефолтом в 1.26 и нужно отключить
GOEXPERIMENT=nogreenteagc go build ./...
- Статус и инструкции подтверждены в блоге Go и в релиз-ноутах 1.25. В 1.26 Green Tea планируют включить по умолчанию, а векторные ускорения добавить на новой x86-архитектуре. go.dev+1
Когда ждать лучший эффект

Green Tea особенно полезен, когда:
- у вас много мелких объектов и длинные цепочки ссылок – раньше это приводило к «рандомным» скачкам по памяти;
- приложение сильно параллельно и работает на многих ядрах – новые структуры работы уменьшают узкие места общей очереди работы GC;
- железо поддерживает широкие векторные инструкции – тогда в 1.26 можно рассчитывать на дополнительный бонус. go.dev
Когда возможны регрессии
- Хип «разрежен» и объекты распределены так, что на странице часто один кандидат для сканирования – накладные расходы на «агрегацию по страницам» могут перекрыть выгоду;
- Профиль приложения изначально почти не упирался в GC – тогда общий выигрыш будет трудно заметить. go.dev
Пример Docker multi-stage
FROM golang:1.25 AS build
WORKDIR /src
COPY . .
# соберём обе версии для A/B
RUN go build -o /out/app-vanilla ./cmd/app && \
GOEXPERIMENT=greenteagc go build -o /out/app-greentea ./cmd/app
FROM gcr.io/distroless/base
COPY --from=build /out/app-greentea /usr/local/bin/app
ENTRYPOINT ["/usr/local/bin/app"]
3) Как правильно мерить
Основная цель Green Tea – уменьшить CPU-стоимость маркировки. Значит, мерим именно это.
- GC trace:
GODEBUG=gctrace=1
Запускаем оба бинаря под одинаковой нагрузкой и сравниваем суммарные времена mark и долю CPU. В блоге есть раздел с объяснением вывода и измерений. Go - pprof CPU: снимаем профили и смотрим вклад функций рантайма
scanobject,greyobject,markrootи т. п.
# пример: HTTP-экспорт pprof в приложении
import _ "net/http/pprof"
# затем
go tool pprof -http=:0 http://localhost:6060/debug/pprof/profile?seconds=60
- Latency SLA: Green Tea сохраняет короткие паузы, но проверяем p95-p99 на своей нагрузке.
- Память: фиксируем пиковый RSS и динамику heap-in-use. Иногда структура хипа немного меняется.
Пример обвязки для A/B
# baseline
GOGC=100 GOMEMLIMIT=0 GODEBUG=gctrace=1 ./app-vanilla 2>trace.vanilla.txt
# green tea
GOGC=100 GOMEMLIMIT=0 GODEBUG=gctrace=1 ./app-greentea 2>trace.greentea.txt
# сравнить суммарные mark total:
grep "gc " trace.* | awk '{print $0}' | head
Нагружающие примеры кода
Ниже три небольших бенчкейса. Они не претендуют на академическую точность, но дают ощущение, где Green Tea раскрывается.
Много мелких объектов с ссылками – типичный выигрыш
// go test -bench=AllocGraph -benchmem -count=5
package gt
import "testing"
type node struct {
next *node
_ [56]byte // делаем объект ~64 байта
}
func makeChain(n int) *node {
var head *node
for i := 0; i < n; i++ {
head = &node{next: head}
}
return head
}
var sink *node
func BenchmarkAllocGraph(b *testing.B) {
for i := 0; i < b.N; i++ {
sink = makeChain(200_000)
}
}
Ожидаемая картина: суммарное время mark снижается, так как объекты одного размера плотно ложатся на страницы 8 KiB и сканируются линейно. Это ровно тот профиль, под который проектировался Green Tea. GitHub
Map с короткоживущими значениями
// go test -bench=MapChurn -benchmem -count=5
package gt
import "testing"
func BenchmarkMapChurn(b *testing.B) {
for i := 0; i < b.N; i++ {
m := make(map[int]*[8]byte, 50_000)
for k := 0; k < 50_000; k++ {
v := new([8]byte) // мелкие объекты
m[k] = v
}
// теряем ссылку на m - очищает GC
}
}
Если у вас сервис активно churn’ит мелкие указательные значения, Green Tea обычно даёт минус 10-30% времени GC. Точные числа зависят от частоты коллекций и давления на память, но именно для такого workload Green Tea и создавался. Go
Анти-пример: по одному объекту на страницу
// go test -bench=Sparse -benchmem -count=5
package gt
import "testing"
// большой объект, чтобы на странице часто оказывался один кандидат
type big struct{ _ [7000]byte }
func BenchmarkSparse(b *testing.B) {
for i := 0; i < b.N; i++ {
s := make([]*big, 2048)
for i := range s {
s[i] = new(big)
}
}
}
Здесь профита может не быть, а иногда будет небольшой регресс – накопление «работы по странице» не окупится, если каждый раз сканируем ровно один объект. В Green Tea есть смягчающий спец-кейс, но он не волшебная палочка. Go
В какую сторону ждать эффекты
Где Green Tea «заходит» лучше всего:
- много мелких объектов и указателей, дерево, граф, индекс в памяти
- много ядер, конкурирующие воркеры GC меньше упираются в общую очередь
- современное x86-железо – с выходом 1.26 добавятся SIMD-ускорения сканирования страниц Go
Где возможны регрессии:
- разреженный хип и частые «одиночки на странице»
- рабочая нагрузка почти не упиралась в GC – общий выигрыш растворится в шуме Go
Чеклист прод-включения
- Соберите два бинаря и прогоните свои нагрузочные тесты.
- Снимите
gctraceиpprof, сравните суммарное время mark и вклад функций GC. Go - Проверьте p95-p99 задержки на приемлемость.
- Прокатите canary на проценте трафика.
- Если заметите аномалии – команда Go просит делиться фидбеком в issue Green Tea. GitHub
Цифры от команды Go
- Внутренние бенчмарки и прод-данные показывают минус 10-40% CPU времени GC без векторных улучшений. Если ваше приложение раньше тратило 10% CPU на GC, то общий выигрыш составит примерно 1-4% CPU. Go
- План: сделать Green Tea дефолтом в Go 1.26, оставить
GOEXPERIMENT=nogreenteagcдля opt-out, добавить SIMD-ускорения на новом x86. Go
Что измерять при тесте у себя
- Время GC и доля CPU GC в профилях
pprof. - Паузу GC – хотя Green Tea и нацелен на CPU, проверьте, не изменились ли задержки в худшую сторону.
- Потребление памяти – новый порядок сканирования может менять локальные пики.
- Стабильность и хвостовые задержки под типичной и пиковыми нагрузками.
Если найдете аномалии – команда Go просит делиться результатами в issue-трекере Green Tea. go.dev
Итог
Green Tea – это не «косметика», а смена единицы работы GC: страницы вместо объектов. Благодаря этому Go лучше использует кэши и префетчеры, снимает часть ограничений на масштабирование по ядрам и в сумме заметно сокращает CPU-накладные GC на реальных сервисах. Включите Green Tea на стенде, прогоните свои бенчмарки и профили – для многих сервисов это простой способ «вернуть» несколько процентов CPU без правок кода. Статус – эксперимент в Go 1.25, план по умолчанию в Go 1.26, с возможностью opt-out. go.dev+1
Полезно знать: формальный трекер дизайна и прогресса Green Tea – в GitHub-issue Go, там же указаны детали для обратной связи.



