🗑 Сборщик мусора в Go: Скрытый налог на ваш CPU Если вы спросите джуна, как работает память в Go, он ответит: "Ну, там есть GC, он сам всё чистит".
Если вы спросите джуна, как работает память в Go, он ответит: "Ну, там есть GC, он сам всё чистит".
Сеньор же знает: GC - это не волшебная фея, это строгий налоговый инспектор. Он может не забирать ваши деньги (память), но он заберет ваше время (CPU).
Давайте развеем главные мифы и заглянем под капот сборщика мусора в Go.
Миф 1: GC вызывает долгие паузы (Stop-The-World)
Выходцы из старой Java (или те, кто писал на Go до версии 1.5) до сих пор пугают детей долгими паузами, когда приложение буквально замирает на секунды.
В современном Go это не так. Наш GC - это Concurrent Mark and Sweep (Конкурентная пометка и очистка). Фаза Stop-The-World (когда тормозится вообще всё) всё ещё есть, но она занимает доли миллисекунды.
Как он работает (Трехцветный алгоритм):
Представьте, что GC - это маляр, который красит ваши объекты:
1. Белые - объекты-кандидаты на удаление (изначально все такие).
2. Серые - объекты, до которых мы смогли дотянуться из корней (глобальные переменные, стеки горутин), но мы еще не проверили, на что ссылаются они сами.
3. Черные - живые объекты, которые мы проверили полностью.
GC бегает по ссылкам, превращая серые объекты в черные, а новые найденные - в серые. Когда серых не остается, все оставшиеся белые объекты просто стираются из памяти.
И главное - он делает это параллельно с работой вашего кода!
В чем подвох? (Mark Assist)
Если паузы такие короткие, почему мы вообще боремся за zero-allocation код?
Потому что чудес не бывает. GC работает в фоне и забирает под себя до 25% CPU (четверть ваших потоков P).
Но это еще не всё. Если ваше приложение генерирует мусор (белые объекты) быстрее, чем фоновый GC успевает их красить, планировщик включает режим паники - Mark Assist.
Он буквально берет вашу горутину, которая обрабатывает важный HTTP-запрос пользователя, и говорит: "Слышь, прежде чем я дам тебе память, иди-ка помоги мне покрасить вон те объекты".
В итоге ваш запрос, который обычно отрабатывает за 5мс, внезапно зависает на 50мс. Вы смотрите в логи и ничего не понимаете.
🔥 Senior Tip: Как этим управлять?
В Go почти нет ручек для тюнинга GC, но есть две самые важные переменные окружения:
1. GOGC (по умолчанию 100). Означает "запускать GC, когда куча выросла на 100% от предыдущего размера". Если у вас куча свободной RAM, ставьте GOGC=500. Мусор будет копиться дольше, GC будет запускаться реже, CPU скажет спасибо.
2. GOMEMLIMIT (появилось в Go 1.19). Это "мягкий" лимит памяти. Позволяет сказать GC: "Не запускайся часто, пока мы не упремся в 2 ГБ, а вот если подошли к лимиту - чисти агрессивно". Это спасение от OOM (Out Of Memory) в Kubernetes.
Мораль: Не бойтесь GC, но уважайте его труд. Используйте sync.Pool, заранее аллоцируйте слайсы (make([]int, 0, capacity)) и не передавайте огромные структуры по значению там, где это не нужно.
#golang #underhood #performance #memory #architecture
👉 @golang_lib