Материал проходит вычитку — возможны неточности.
Урок 22 из 118

GOGC и GOMEMLIMIT

«Полка» переехала в контейнер с лимитом памяти 512 МБ и в первую же ночь словила OOM-kill. Сергей поставил задачу: настроить память так, чтобы под перестал убивать сервис.

Главная мысль

GOGC — ручка обмена памяти на CPU: он задаёт целевой размер кучи (heap goal); GOMEMLIMIT — мягкий лимит памяти, чтобы жить в контейнере, но от OOM сам по себе не защищает.

GOGC

Основная ручка настройки — переменная окружения GOGC. По умолчанию GOGC=100, и это процент, задающий целевой размер кучи — heap goal: примерно живое + (живое + корни GC) × GOGC/100, корни учитываются с Go 1.18. Запускает сборку пейсер раньше цели: если после сборки живо 100 МБ, следующая начнётся где-то на пути к 200 МБ, а не ровно на 200.

  • GOGC=200 — сборки вдвое реже, куча растёт вдвое больше.
  • GOGC=50 — сборки вдвое чаще, куча меньше.
  • GOGC=off — отключает сборку по порогу роста: куча будет расти, пока не упрётся в лимит. Без лимита это путь к OOM.

Это прямой обмен памяти на процессор: чаще собирать — меньше памяти, но больше CPU на сборку; реже — наоборот.

GOMEMLIMIT

С Go 1.19 есть GOMEMLIMIT — мягкий лимит на память, которой распоряжается сам рантайм. Появился как ответ на контейнеры: у пода в Kubernetes жёсткий лимит памяти, и процесс, который о нём не знает, рискует быть убитым по OOM.

Мягкость — сразу в двух местах. Лимит считает кучу Go вместе со стеками горутин и служебными структурами рантайма, но не внешнюю память: сам бинарник, память cgo и всё, что ОС держит вне контроля Go. А ещё лимит — цель, а не стена: куча может превышать его не только коротким всплеском. При большом живом наборе или когда GC ограничен по CPU (рантайм отводит сборке примерно половину CPU-времени) превышение держится подолгу, а слишком низкий лимит даёт thrashing. Поэтому GOMEMLIMIT от OOM не защищает.

Типовая настройка

Когда куча приближается к GOMEMLIMIT, GC начинает собирать чаще, даже если GOGC говорит «ещё рано». При GOGC=off и заданном лимите сборки всё равно будут, чтобы держаться в нём. Типичная настройка для сервиса в контейнере: оставить GOGC по умолчанию или поднять его, а GOMEMLIMIT поставить чуть ниже лимита пода, оставив запас под cgo и всё, что в лимит не входит.

В следующем уроке

Вы узнаете:

  • как debug.SetMemoryLimit задаёт лимит из кода
  • почему GOMEMLIMIT не заменяет лимит пода
Задание · вопросы

Проверьте себя

Вопрос 1 из 3

Что делает GOGC=200 по сравнению с GOGC=100?

Вопрос 2 из 3

Защищает ли GOMEMLIMIT от OOM kill?

Вопрос 3 из 3

Что произойдёт при GOGC=off без GOMEMLIMIT?

Итоги

GOGC задаёт целевой размер кучи — память против CPU; GOMEMLIMIT — мягкий лимит под контейнер, который от OOM не спасает. Пара «GOGC побольше + GOMEMLIMIT с запасом» — стандарт для подов.

Наш сайт собирает куки. Чтобы тебе было удобнее им пользоваться. Нажимая на кнопку, ты даёшь разрешение собирать куки