♻️ sync.Pool: Как перестать кормить Garbage Collector'а — 10 августа 2026 г. в 18:16:09.009
♻️ sync.Pool: Как перестать кормить Garbage Collector'а Представьте, что вы работаете в кофейне. Приходит клиент, вы покупаете новую керамическую кружку, наливаете кофе, отдаете клиенту. Он выпивает кофе и... выбрасывает кружку в мусорку. А вы идете покупать новую. Звучит как бред? Но именно так ведет себя ваш код, когда вы создаете буферы make([]byte, 4096) внутри HTTP-хендлера, который обрабатывает 10 000 запросов в секунду. Вы непрерывно выделяете память, а Garbage Collector (тот самый уборщик из прошлых постов) сходит с ума, пытаясь всё это собрать. Сервис тормозит, CPU кипит. Решение проблемы sync.Pool. Это полка с чистыми кружками. Как это работает: Вместо того чтобы создавать объект с нуля, вы просите его у пула (Get). Попользовались - помыли - вернули в пул (Put). ✅ Как это выглядит в коде: var bufPool = sync.Pool{ // Эта функция вызовется ТОЛЬКО если пул пуст // и нам действительно нужно создать новый объект New: func() any { // Выделяем память один раз! return new(bytes.Buffer) }, } func HandleRequest(w http.ResponseWriter, r *http.Request) { // 1. Берем буфер из пула (приводим тип, так как Pool возвращает any) buf := bufPool.Get().(*bytes.Buffer) // 2. ОБЯЗАТЕЛЬНО: Возвращаем буфер в пул при выходе из функции defer bufPool.Put(buf) // 3. КРИТИЧЕСКИ ВАЖНО: Очищаем буфер перед использованием! buf.Reset() // ... используем buf для json.Marshal или io.Copy ... buf.WriteString("hello, world") } 🔥 Нюансы для Senior-ов (Ловушки sync.Pool): 1. Грязные кружки (Утечка данных). Если вы забыли сделать buf.Reset() перед тем как положить буфер обратно (или сразу после того, как достали), следующий гость получит кружку с остатками чужого кофе. В мире микросервисов это значит, что ответ одному пользователю может случайно содержать кусок JSON-а с приватными данными предыдущего пользователя. Это жесточайшая уязвимость. 2. Это не кэш для бизнес-данных! Многие думают: "О, круто, положу-ка я туда настройки из БД, чтобы не ходить за ними каждый раз". Нет. Garbage Collector в Go имеет полное право (и делает это) полностью очистить весь sync.Pool во время сборки мусора. Пул может опустеть в любую миллисекунду. Он предназначен только для переиспользования пустой памяти, а не для хранения состояния. 3. Под капотом: Никаких блокировок (почти). Зачем использовать sync.Pool, а не написать свой кэш на каналах или мьютексах? Потому что sync.Pool гениально оптимизирован под планировщик Go (GMP). Он хранит локальный пул для каждого логического ядра (P). Когда горутина просит объект, она берет его из пула своего ядра вообще без блокировки (lock-free). Мьютексы включаются только тогда, когда локальный пул пуст и нужно "украсть" объект у соседнего ядра. Используйте sync.Pool для []byte, bytes.Buffer, сложных структур парсинга - и ваши графики CPU станут плоскими, как кардиограмма после дедлайна. #golang #performance #memory #underhood #bestpractices 👉 @golang_lib

