Вопрос с собеседования · Go

Что такое утечка горутин и как её избежать?

MiddleГорутины и каналы
Ещё спрашивают так:
  • Почему растёт число горутин в сервисе?
Коротко

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

  • У каждой горутины должен быть понятный способ завершиться
  • Отмена через context и select с ctx.Done()
  • Буфер на 1 для результата позволяет отправителю завершиться, даже если ответ не нужен

А ещё Найти: runtime.NumGoroutine, pprof goroutine-профиль, goleak в тестах

Подробно

Почему это серьёзно. Сборщик мусора не освобождает заблокированные горутины — с точки зрения рантайма они живы. В долгоживущем сервисе их число растёт с каждым запросом, а с ними память и открытые соединения.

Правила:

  • запуская горутину, знать, когда и как она завершится;
  • передавать context и проверять ctx.Done() в циклах и select;
  • у внешних вызовов — таймауты (http.Client{Timeout}, context.WithTimeout);
  • закрывать каналы, по которым читают через range;
  • результат «может быть никому не нужен» — канал с буфером 1.

Диагностика: метрика go_goroutines, /debug/pprof/goroutine?debug=1 показывает стеки — тысячи одинаковых стеков указывают на место утечки; go.uber.org/goleak проверяет в тестах, что горутин не осталось.

Пример

// утечка: при таймауте горутина навсегда висит на отправке
func fetch(ctx context.Context) (string, error) {
    ch := make(chan string)
    go func() { ch <- slowCall() }()
    select {
    case r := <-ch:
        return r, nil
    case <-ctx.Done():
        return "", ctx.Err()
    }
}
// исправление: make(chan string, 1) — отправка не заблокируется

Что хотят услышать

  • Что такое утечка и типичные причины.
  • Как предотвращать.
  • Как обнаружить.

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

Один верный вариант. Подумайте, потом откройте ответ.

Показать варианты и ответ
  • ✗Горутины завершаются сами при выходе из функции, которая их запустила, и GC их убирает
  • ✗Сборщик мусора удаляет заблокированные горутины
  • ✗Утечка горутин не занимает память
  • ✓Горутина, навсегда заблокированная на канале; нужна отмена через context

Связанные вопросы

Партнёр платформы

Разбор собеседования с sotto

После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.

Разбор собеседования