Что такое Go runtime и из каких ключевых компонентов он состоит?

Ответ

Go runtime — это среда выполнения, которая вкомпилирована в каждую Go-программу. Она управляет высокоуровневыми возможностями языка, такими как горутины, каналы, сборка мусора и взаимодействие с операционной системой.

Ключевые компоненты Go runtime:

  1. Планировщик горутин (Goroutine Scheduler)

    • Реализует модель мультиплексирования M:N, отображая M горутин на N потоков ОС. Это позволяет эффективно управлять тысячами горутин на небольшом количестве потоков.
    • Работает по модели GMP: G (Goroutine) — легковесный поток, M (Machine) — поток ОС, P (Processor) — логический процессор, выполняющий горутины. Планировщик распределяет G по свободным P, которые выполняются на M.
  2. Сборщик мусора (Garbage Collector, GC)

    • Автоматически освобождает память, которая больше не используется программой.
    • В Go используется конкурентный трехцветный сборщик мусора (tri-color mark-and-sweep), который работает параллельно с основной программой и имеет очень короткие паузы (stop-the-world), что критично для высокопроизводительных приложений.
  3. Управление памятью (Memory Management)

    • Runtime управляет выделением памяти как в стеке (для каждой горутины свой небольшой, расширяемый стек), так и в куче (для объектов, время жизни которых неизвестно на этапе компиляции).
  4. Управление горутинами (Goroutine Management)

    • Отвечает за создание, планирование, приостановку и возобновление горутин. Поскольку горутины управляются runtime, а не ядром ОС, их создание и переключение контекста происходят очень быстро.
  5. Взаимодействие с ОС (OS Interaction)

    • Runtime абстрагирует системные вызовы. Например, при блокирующей операции ввода-вывода (I/O), такой как чтение из сети, runtime не блокирует поток ОС (M), а переключает его на выполнение другой горутины (G), пока операция не завершится.

Пример взаимодействия с runtime для получения информации о среде:

package main

import (
    "fmt"
    "runtime"
)

func main() {
    // Получаем количество логических процессоров, доступных для runtime
    fmt.Printf("Количество CPU: %dn", runtime.NumCPU())

    // GOMAXPROCS определяет, сколько потоков ОС могут одновременно выполнять код Go.
    // Значение 0 означает, что будет использовано количество, равное NumCPU().
    fmt.Printf("GOMAXPROCS: %dn", runtime.GOMAXPROCS(0))

    // Получаем количество активных горутин (в данном случае будет 1 - main)
    fmt.Printf("Количество горутин: %dn", runtime.NumGoroutine())
}

Ответ 18+ 🔞

А, вот ты про рантайм Go спрашиваешь? Ну, это, блядь, такая хитрая жопа, которая вшита в каждый твой бинарь. Она там, сука, как невидимый дирижёр оркестра, который горутинами дирижирует, память подтирает и с операционкой договаривается. Без неё твоя программа — просто кусок мёртвого кода, который нихуя не может.

Слушай, а вот главные её трюки, на которых всё держится:

  1. Планировщик горутин (Goroutine Scheduler)

    • Это, ёпта, главный фокус-покус. Он умеет из тысячи лёгких горутин (G) натянуть на пару десятков толстых потоков ОС (M) через эти вот логические процессоры (P). Представь, будто ты один человек (поток ОС), но у тебя овердохуища задач (горутин), и ты между ними переключаешься так быстро, что всем кажется, будто ты делаешь всё сразу. Вот это он и есть.
  2. Сборщик мусора (Garbage Collector, GC)

    • А это, блядь, самый ответственный дворник. Ты накушаешься в памяти, насоздаёшь объектов, а он потом приходит и всё, что не приколочено, выносит. И делает это так, что ты почти не замечаешь — работает параллельно, паузы у него микроскопические. Трёхцветный, конкурентный, красавчик, одним словом. Хуй с горы, а не сборщик.
  3. Управление памятью (Memory Management)

    • Тут он, сука, как рачительный хозяин. У каждой горутинки свой маленький столик-стек, который можно при необходимости расширить. А если уж объект такой важный, что должен жить долго и счастливо, то его отправляют в общую кладовку — кучу. И за всем этим порядком следит наш рантайм.
  4. Управление горутинами (Goroutine Management)

    • Он их создаёт из воздуха, укладывает спать, когда они ждут ответа от сети, и будит, когда ответ пришёл. И всё это без лишнего шума и пыли, без вечных жалоб ядру операционки. Просто потому что может.
  5. Взаимодействие с ОС (OS Interaction)

    • Он, блядь, как переводчик с языка Go на язык системы. Горутина захотела почитать из сокета — рантайм не даст потоку ОС заснуть, а скажет: «Слушай, братан, иди пока другую горутину покрути, а я тут посижу, подожду ответа». И эффективность, сука, зашкаливает.

Ну и вот, чтобы не быть голословным, смотри, как можно с этим рантаймом пообщаться, спросить, как у него дела:

package main

import (
    "fmt"
    "runtime"
)

func main() {
    // Спрашиваем, на скольких ядрах можем развернуться
    fmt.Printf("Количество CPU: %dn", runtime.NumCPU())

    // А сколько потоков ОС нам сейчас разрешено занять? Ноль — значит, спрашиваем текущее значение.
    fmt.Printf("GOMAXPROCS: %dn", runtime.GOMAXPROCS(0))

    // И сколько у нас сейчас этих самых горутин носятся? Пока одна — main.
    fmt.Printf("Количество горутин: %dn", runtime.NumGoroutine())
}

Вот такая она, магия под капотом. Выглядит просто, а внутри, блядь, целый ёперный театр с балансировкой, уборкой и дипломатией. Чистый изумруд, а не система!