Ответ
Сборщик мусора (Garbage Collector, GC) в Go работает исключительно с кучей (heap) — областью памяти, где размещаются динамически выделенные объекты (например, через make()
, new()
или при взятии адреса у составных литералов).
Память на стеке (stack) (локальные переменные функций, аргументы) управляется автоматически и освобождается при выходе из области видимости (при завершении функции). GC ее не касается.
Ключевые характеристики GC в Go:
Параллельный (Concurrent): GC работает в фоновом режиме, в отдельных горутинах, параллельно с основной программой. Это позволяет минимизировать паузы (stop-the-world), во время которых выполнение пользовательских горутин полностью останавливается. Паузы в современном Go GC измеряются в микросекундах.
Трехцветный алгоритм маркировки и очистки (Tri-color mark-and-sweep): Это алгоритм, который позволяет GC работать параллельно с программой. Объекты условно делятся на три множества:
- Белые: Объекты-кандидаты на удаление.
- Серые: Объекты, которые достижимы из корневых объектов (глобальные переменные, стеки горутин), но их дочерние объекты еще не просканированы.
- Черные: Объекты, которые точно достижимы и все их дочерние объекты просканированы.
GC ищет и помечает все достижимые объекты (перекрашивая их из белого в серый, а затем в черный), а в конце фазы очистки удаляет все оставшиеся "белые" (недостижимые) объекты.
Неперемещающий (Non-moving): В отличие от GC в некоторых других языках (например, Java), Go GC не перемещает объекты в памяти для дефрагментации кучи. Это упрощает написание кода на CGo и взаимодействие с указателями.
Управляется через
GOGC
: Переменная окруженияGOGC
контролирует, когда запускается следующий цикл сборки мусора. Значение по умолчанию100
означает, что новый цикл начнется, когда размер кучи после предыдущей сборки увеличится на 100% (т.е. вдвое).GOGC=off
отключает GC.
Пример:
func createObject() *int {
// `make` выделяет память в куче для слайса.
// Эта память будет управляться GC.
data := make([]int, 1000)
// `new` также выделяет память в куче.
// Указатель на эту память покидает область видимости функции,
// но сама память останется, пока на нее есть ссылки.
obj := new(int)
*obj = 42
_ = data // Используем data, чтобы компилятор не оптимизировал
return obj
}
func main() {
// p ссылается на объект в куче.
p := createObject()
// После того как p выйдет из области видимости (или ему будет присвоен nil),
// объект *int станет недостижимым, и GC сможет его собрать.
fmt.Println(*p)
}