Ответ
В Go используется полностью автоматический сборщик мусора (Garbage Collector, GC), который самостоятельно освобождает память, занятую объектами, ставшими недостижимыми из кода программы (например, локальные переменные после выхода из функции).
Основу GC в Go составляет неблокирующий (concurrent) алгоритм mark-and-sweep (пометь и убери).
Ключевые особенности:
Concurrent (Параллельный): GC работает параллельно с основной программой (горутинами), а не останавливает её на всё время своей работы. Это приводит к очень коротким паузам (stop-the-world pauses), обычно менее миллисекунды, что критически важно для высоконагруженных и low-latency систем.
Трехцветный алгоритм: Это вариация mark-and-sweep. Объекты в памяти условно окрашиваются в один из трех цветов для отслеживания достижимости:
- Белый: Потенциальный мусор.
- Серый: Объект достижим, но его дочерние объекты еще не проверены.
- Черный: Объект достижим, и все его дочерние объекты проверены.
Управление через
GOGC
: Поведение GC можно настраивать через переменную окруженияGOGC
. Она задает процент, на который должна вырасти куча (heap) по сравнению с её размером после прошлой сборки, чтобы запустить новую. По умолчаниюGOGC=100
, что означает, что новая сборка мусора начнется, когда размер используемой памяти в куче удвоится.
Пример для демонстрации (не для реального кода):
В реальных приложениях вызывать GC вручную не нужно. Код ниже просто показывает, как GC освобождает память.
package main
import (
"fmt"
"runtime"
)
func printMemStats() {
var m runtime.MemStats
runtime.ReadMemStats(&m)
// HeapAlloc - это байты, выделенные и все еще используемые.
fmt.Printf("HeapAlloc = %v MiBn", m.HeapAlloc / 1024 / 1024)
}
func main() {
printMemStats()
// Создаем большой объект, который станет мусором после выхода из функции.
_ = make([]byte, 20<<20) // 20 MB
printMemStats()
// Принудительно запускаем GC для демонстрации.
// В реальном коде это делать не рекомендуется!
runtime.GC()
fmt.Println("nAfter GC:")
printMemStats()
}