Senior
692
questionbank

Как устроен runtime в Go?

Получайте ответы на вопросы в реальном времени с Sobes Copilot
Ответ от нейросети sobes.tech

Runtime Go состоит из следующих ключевых компонент: планировщик (scheduler), сборщик мусора (garbage collector) и система goroutine'ов.

  1. Планировщик (ोसcheduler): Реализует многопоточность в пространстве пользователя (user-space threading). Он сопоставляет M (many) пользовательских горутин с N (few) потоками операционной системы. Использует модель M:N, которая эффективнее, чем 1:1 (каждая горутина — отдельный поток ОС) или N:1 (все горутины — один поток ОС). Планировщик управляет тремя очередями:

    • Глобальная очередь (global run queue): горутины, которые еще не assigned к P.
    • Локальная очередь (local run queue): горутины, assigned к конкретной P.
    • Очередь ожидания (wait queue): горутины, заблокированные по внешним причинам (сеть, файловый ввод/вывод).

    Модель планировщика основана на P (processor) - логическом процессоре, который связывает G (goroutine) и M (OS thread). M выполняет код G, а P предоставляет ресурсы и контекст для выполнения (локальную очередь горутин, кеш). Планировщик распределяет горутины между доступными M и P.

  2. Сборщик мусора (Garbage Collector - GC): В Go используется конкурентный и параллельный сборщик мусора. Он работает concurrently с пользовательским кодом (stop-the-world фазы минимизированы) и параллельно (использует несколько ядер CPU). Алгоритм Mark-and-Sweep с триколорной схемой.

    Финализация объектов также осуществляется runtime'ом.

  3. Goroutine'ы: Легковесные, конкуретные функции, управляемые runtime Go, а не ОС. Они требуют меньше памяти (изначально 2KB стека, который может расти) и переключение между ними быстрее, чем между потоками ОС. Создаются с помощью ключевого слова go.

    Коммуникация между горутинами происходит через каналы (channels), которые являются безопасным способом обмена данными и синхронизации.

    go
  4. Система ввода/вывода (I/O): Runtime Go использует неблокирующие I/O операции и мультиплексирование событий (например, epoll на Linux, kqueue на FreeBSD/macOS, IOCP на Windows). Когда горутина блокируется на I/O, runtime отсоединяет ее от M, позволяет M выполнять другую горутину, а когда I/O завершен, планировщик снова назначает горутине M (возможно, другую). Это позволяет эффективно использовать системные ресурсы и избегать блокировки потоков ОС.

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

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