Ответ
Garbage Collector (GC) в Go автоматически управляет памятью, освобождая неиспользуемые объекты в куче (heap). Это избавляет разработчика от ручного управления памятью (как в C/C++), предотвращая утечки памяти и ошибки, связанные с неправильным освобождением.
Основные особенности GC в Go:
- Неблокирующий (Concurrent): Большая часть работы GC выполняется параллельно с выполнением основной программы, минимизируя паузы (stop-the-world).
- Триколорная маркировка: Использует алгоритм маркировки объектов (белый, серый, чёрный) для определения достижимых объектов.
- Короткие "stop-the-world" паузы: Хотя GC в Go в основном конкурентный, короткие фазы "stop-the-world" (когда выполнение программы приостанавливается) всё же присутствуют, но их длительность обычно измеряется микросекундами.
- Настраивается через
GOGC
: Переменная окруженияGOGC
позволяет контролировать, насколько сильно должна вырасти куча перед запуском следующего цикла сборки мусора (по умолчанию 100%).
Пример утечки памяти (если бы GC отсутствовал или был неэффективен):
package main
import (
"fmt"
"runtime"
"time"
)
func main() {
var data []*int
fmt.Println("Начало работы, нажмите Ctrl+C для выхода.")
for i := 0; ; i++ {
// В реальной ситуации, если бы GC не работал, или мы бы не освобождали
// память явно, этот цикл приводил бы к постоянному росту потребления памяти.
// В Go GC автоматически освобождает неиспользуемые объекты.
// Однако, если мы постоянно сохраняем ссылки на объекты, они не будут собраны.
val := i // Создаем новую переменную на каждой итерации
data = append(data, &val) // Сохраняем указатель на 'val'
if i%100000 == 0 {
fmt.Printf("Итерация %d, количество элементов: %d, Память: %d MBn", i, len(data), getMemUsageMB())
// В этом примере 'data' постоянно растет, и GC не может освободить 'val',
// так как на нее есть ссылка. Это демонстрирует, как можно создать
// логическую утечку, даже при наличии GC.
}
time.Sleep(time.Millisecond) // Небольшая задержка для наглядности
}
}
func getMemUsageMB() uint64 {
var m runtime.MemStats
runtime.ReadMemStats(&m)
return m.Alloc / 1024 / 1024
}
GC особенно важен для долгоживущих сервисов (например, веб-серверов, микросервисов) и при работе с большими или динамически изменяющимися структурами данных, где ручное управление памятью было бы сложным и подверженным ошибкам.