Ответ
Главный принцип оптимизации GC в Go — уменьшение количества и размера аллокаций в куче (heap). Чем меньше мусора создается, тем реже и быстрее работает сборщик.
Основные методы:
-
Уменьшение аллокаций в куче.
- Переиспользуйте память. Вместо создания новых слайсов или буферов в циклах, создавайте их один раз и используйте повторно.
-
Предварительно выделяйте память. Если размер слайса или карты известен заранее, используйте
makeс указанием емкости (capacity). Это предотвратит лишние переаллокации.// Плохо: слайс будет многократно переаллоцироваться var s []int for i := 0; i < 1000; i++ { s = append(s, i) } // Хорошо: одна аллокация нужного размера s := make([]int, 0, 1000) for i := 0; i < 1000; i++ { s = append(s, i) }
-
Использование
sync.Pool. Идеально подходит для переиспользования временных, недолговечных объектов, которые интенсивно создаются и уничтожаются (например, буферы, объекты для сериализации). Это снижает давление на GC.var bufferPool = sync.Pool{ New: func() interface{} { return new(bytes.Buffer) }, } buf := bufferPool.Get().(*bytes.Buffer) buf.Reset() // Очищаем буфер перед использованием // ... работа с буфером ... defer bufferPool.Put(buf) -
Профилирование. Это первый и главный шаг любой оптимизации. Используйте
go tool pprofдля анализа профиля памяти (-alloc_objects,-alloc_space), чтобы найти участки кода, которые создают больше всего мусора. -
Осторожное использование указателей. GC сканирует объекты в куче, следуя по указателям. Большие структуры данных или слайсы, не содержащие указателей, сканируются сборщиком мусора гораздо быстрее. По возможности, избегайте указателей в больших структурах данных.
-
Настройка GC с помощью
GOGC. Переменная окруженияGOGC(по умолчанию100) определяет, при каком соотношении новой и старой памяти запускать GC.GOGC=200запустит GC, когда объем памяти в куче увеличится на 200% (в 3 раза). УвеличениеGOGCснижает частоту запусков GC (уменьшая нагрузку на CPU) ценой большего потребления памяти. Это грубый инструмент, который следует использовать с осторожностью.
Ответ 18+ 🔞
Слушай, а вот этот твой сборщик мусора в Go — он, конечно, не дурак, но если его постоянно кормить говном, то он и работать будет, как говно. Главная мысль, которую надо вбить себе в башку: меньше аллокаций в куче — меньше работы для GC. Чем меньше мусора ты создаёшь, тем реже эта мартышлюшка будет бегать по твоей памяти с метлой.
Вот на чём можно сэкономить:
-
Хули ты каждый раз новое ведро покупаешь? Переиспользуй, блядь! Вместо того чтобы в цикле каждый раз создавать новый слайс или буфер, возьми один, отмой его и юзай снова. Экономия — мать порядка, ёпта.
-
Предсказывай будущее, ясновидящий блядь. Если ты заранее знаешь, сколько элементов будет в слайсе или карте, выдели память сразу! Используй
makeс ёбаной ёмкостью (capacity). Это избавит тебя от лишних переездов данных в памяти, которые так любитappend.// Пиздец как плохо: слайс будет кряхтеть и расширяться на каждой итерации var s []int for i := 0; i < 1000; i++ { s = append(s, i) } // О, красота: один раз выделил и забыл, как страшный сон s := make([]int, 0, 1000) for i := 0; i < 1000; i++ { s = append(s, i) } -
sync.Pool— твой лучший друг для временщиков. Есть объекты, которые живут три секунды, но создаются тысячами? Буферы, энкодеры, всякая такая хуйня. Не давай им умирать! Скидывай в общий бассейн (Pool) и потом доставай оттуда же, отмытого. Давление на GC упадёт просто овердохуища.var bufferPool = sync.Pool{ New: func() interface{} { return new(bytes.Buffer) // Создаст новый, если в бассейне пусто }, } buf := bufferPool.Get().(*bytes.Buffer) // Достал из бассейна buf.Reset() // Важный момент! Отмыл перед использованием, а то там старые сопли будут // ... делаешь с буфером что хош ... defer bufferPool.Put(buf) // Вернул обратно, хороший мальчик -
Профилирование — это святое, ёбаный насос! Не гадай на кофейной гуще, где у тебя аллокации! Запусти
go tool pprof, посмотри профили памяти (-alloc_objects,-alloc_space). Узнаешь, какой кусок кода родил больше всего мусора, и будешь бить точно в голову, а не по хвостам. -
Указатели — они как спички, детям не игрушка. GC, когда работает, бегает по всем указателям в куче. Если у тебя огромная структура данных без единого указателя внутри — он её просканирует моментально. А вот если там натыкано
*SomeShit— всё, пидарас шерстяной, будет копаться дольше. Где можно — избегай указателей в больших структурах. -
Кнопка «Тихо, GC, работаю!» —
GOGC. Это переменная окружения. По умолчанию стоит100. Это значит «запускай сборку, когда мусора станет в два раза больше, чем живой памяти». Если поставитьGOGC=200, то GC будет запускаться реже (меньше нагрузка на CPU), но твоя программа начнёт жрать больше памяти. Инструмент грубый, как лом, но иногда полезный. Главное — не выстрели себе в ногу.