Создается ли при каждом вызове `go` с нуля новая структура для горутины?

Ответ

Не совсем. Для каждой новой горутины действительно выделяется своя уникальная структура g, которая хранит её состояние (стек, счётчик команд и т.д.). Однако Go runtime активно оптимизирует этот процесс, используя пул (pool) уже созданных структур g.

Как это работает:

  1. Когда горутина завершает свою работу, её структура g не уничтожается сразу, а помещается в специальный список свободных структур.
  2. При запуске новой горутины (go myFunc()) runtime сначала пытается взять готовую структуру g из этого пула.
  3. Если пул пуст, только тогда будет выделена память под новую структуру g.

Этот механизм называется переиспользованием (pooling) и позволяет значительно снизить нагрузку на сборщик мусора и уменьшить накладные расходы на создание и уничтожение горутин, делая их запуск очень "дешевой" операцией.

Пример

func main() {
    // Запускаем 1000 горутин
    for i := 0; i < 1000; i++ {
        go func(n int) {
            // какая-то работа
        }(i)
    }
    // Runtime будет активно переиспользовать структуры `g`
    // для этих горутин, а не создавать все 1000 с нуля.
}

Вывод: Хотя каждая горутина логически является новой и имеет свою структуру g, физически эта структура может быть не создана с нуля, а взята из пула для переиспользования.