Выпуск новостей завершён 15 сентября 2026. Материалы сохранены на дату публикации. Перейти к инструкциям →

Архив · Полный материал · Tproger

Go 1.27 находит утечки горутин в работающем сервисе через pprof

Профиль goroutineleak в Go 1.27 находит горутины, навсегда заблокированные на каналах, Mutex, WaitGroup и Cond, в продакшене: как включить и что он не ловит.

Безопасность · TprogerПолина Ярцева5 минут
Перейти к тексту ↓
Go 1.27 находит утечки горутин в работающем сервисе через pprof
Tproger
Коротко о главномРазвернутьСвернуть
  1. Команда Go 2 сентября рассказала в официальном блоге о новом типе профиля goroutineleak , который вошёл в Go 1.
  2. Он находит горутины, навсегда заблокированные на каналах и примитивах пакета sync , и делает это на работающем продакшен-сервисе, без тестов и без остановки процесса.
  3. Профиль доступен через runtime/pprof и через стандартный HTTP-обработчик net/http/pprof по адресу /debug/pprof/goroutineleak .

Команда Go 2 сентября рассказала в официальном блоге о новом типе профиля goroutineleak , который вошёл в Go 1.27. Он находит горутины, навсегда заблокированные на каналах и примитивах пакета sync , и делает это на работающем продакшен-сервисе, без тестов и без остановки процесса. Профиль доступен через runtime/pprof и через стандартный HTTP-обработчик net/http/pprof по адресу /debug/pprof/goroutineleak .

Для тех, кто держит Go-сервисы в проде, это закрывает старую дыру в инструментах. Обычный goroutine-профиль показывает, сколько горутин сейчас заблокировано, но не отличает штатное ожидание от горутины, которая уже никогда не проснётся. Раньше такие утечки искали руками по стекам или ловили в тестах пакетом goleak ; сам релиз Go 1.27 вышел 19 августа, а разбор механизма команда опубликовала только сейчас.

Что такое утечка горутины и почему её было трудно найти

Утечка горутины по определению из блога Go: горутина заблокирована на операции, условие для продолжения которой больше никогда не наступит. Классический пример: воркеры пишут результаты в небуферизованный канал, а читатель вышел из функции по первой ошибке. Каждый следующий воркер зависает на ch - result навсегда, и чем дольше живёт процесс, тем больше таких горутин копится, растёт потребление памяти и нагрузка на сборщик мусора.

До Go 1.27 инструментов было три, и ни один не решал задачу для продакшена. goleak проверяет, не остались ли горутины после конкретного теста. Пакет synctest , появившийся в стандартной библиотеке Go 1.25, помогает тестировать конкурентный код с виртуальным временем. Обычный goroutine-профиль показывает заблокированные горутины, но не доказывает, что они не проснутся: всплеск нагрузки выглядит в нём так же, как утечка.

Как профиль отличает утечку от штатной блокировки

Идея, которую описывает автор поста Влад Сайок из команды Go, опирается на работу, которую сборщик мусора и так делает. GC вычисляет достижимость памяти от корней. Команда добавила к этому анализу горутины: горутина считается живой, если она не заблокирована на примитиве конкурентности или если примитив, на котором она ждёт, достижим из какой-нибудь живой горутины. Анализ стартует с незаблокированных горутин и распространяется по доступным им каналам и мьютексам. Всё, что осталось недостижимым после этого обхода, никто уже не разблокирует, значит, это утечка.

Команда Go утверждает, что механизм точный и «почти не даёт ложных срабатываний» (перевод редакции). Есть патологический случай: цепочка горутин, где каждая ждёт примитив, доступный только следующей. Для такой «гирлянды» проверка в худшем случае занимает O(n²) шагов за один цикл GC, поэтому в блоге советуют не включать сбор профиля постоянно, а снимать его периодически, например раз в четыре часа. Память на учёт горутин авторы называют пренебрежимо малой, а GC при этом продолжает работать параллельно с пользовательским кодом.

Разработка выросла из совместного исследования Орхусского университета, Университета Вашингтона в Сент-Луисе и Uber; академическую версию представили на конференции ASPLOS 2025.

Что видно в профиле на живом примере

В блоге разобран сервис, который раз в секунду запускает обработку десяти элементов и на пятом получает ошибку. Функция делает ранний return , оставшиеся воркеры зависают на отправке в канал. Программа подключает net/http/pprof и слушает localhost:6060 ; профиль снимают обычным способом:

curl -o leak.prof http://localhost:6060/debug/pprof/goroutineleak
go tool pprof leak.prof

Через несколько минут работы pprof показывает Total: 116 заблокированных горутин, и все они стоят на одной операции: отправке в канал ch . Исправление для примера тривиальное: сделать канал буферизованным на число воркеров, make(chan result, len(ws)) , тогда воркеры допишут результаты и завершатся, даже если читатель ушёл.

Что профиль не ловит

  • Ожидание файлового и сетевого ввода-вывода и системных вызовов утечкой не считается: горутина, зависшая на чтении из сокета, в профиль не попадёт.
  • Самодельные спинлоки и другие пользовательские механизмы блокировки не входят в гарантированный набор.
  • Горутина, которая по замыслу ждёт вечно на достижимом канале (например, фоновый слушатель), утечкой не считается, потому что канал достижим из живого кода.

Кому новый профиль ничего не даст: сервисам, где горутины блокируются в основном на сети, а не на каналах, и проектам, которые ещё не перешли на Go 1.27. Профиль работает только в рантайме этой версии; для старых сборок остаются goleak и ручной разбор стеков.

Как включить у себя

  1. Обновить тулчейн до Go 1.27 (релиз от 19 августа 2026 года, заметки к выпуску ) и пересобрать сервис.
  2. Если net/http/pprof уже подключён, новый обработчик появится автоматически по пути /debug/pprof/goroutineleak ; отдельной настройки не нужно.
  3. Снимать профиль периодически, а не постоянно: команда Go предлагает интервал вроде четырёх часов из-за квадратичного худшего случая.
  4. Смотреть в первую очередь на места с ранним return при ошибках, таймауты без select с контекстом и воркер-циклы, у которых читатель может уйти раньше писателей.

Схемы GC и полный пример кода лежат в посте команды Go . Из соседних новостей по инфраструктуре Go-сервисов на сайте есть разбор Kubernetes 1.37 и заметка о tailcat от Tailscale , написанном на Go.

Источники: Go Blog: Goroutine leak profiles , Go 1.27 Release Notes , Go Blog: Go 1.27 is released

Изображение на обложке: Go Authors, логотип Go