sync.Pool был введён в Go для повторного использования временных объектов и снижения давления на сборщик мусора. Исторически разработчики создавали собственные пулы объектов, используя мьютексы, но это приводило к сложному коду и возможным ошибкам синхронизации. sync.Pool является потокобезопасной структурой из стандартной библиотеки, предназначенной для производства объекта при первом обращении, хранения и последующего возврата этого объекта в пул для повторного использования.
Проблема — при большом количестве короткоживущих объектов (например, буферы в HTTP-сервере) система часто тратит много времени на аллокации. Использование sync.Pool позволяет факультативно кэшировать объекты, не гарантируя их сохранение в памяти, но повышая производительность за счёт уменьшения числа сборок мусора.
Решение — использовать Pool для временных, однородных, быстроиспользуемых структур (например, bytes.Buffer), возвращая объект в пул методом Put и извлекая через Get. Объекты могут быть удалены из пула в любой момент (например, при запуске GC), поэтому не годится для долговременного хранения.
Пример кода:
import ( "sync" "bytes" "fmt" ) var bufPool = sync.Pool{ New: func() interface{} { return new(bytes.Buffer) }, } func main() { b := bufPool.Get().(*bytes.Buffer) b.Reset() b.WriteString("hello, world!") fmt.Println(b.String()) bufPool.Put(b) // обязательно возвращаем }
Ключевые особенности:
Может ли sync.Pool быть использован для долговременного хранения состояния?
Нет, любые объекты в Pool могут быть удалены системой в любой момент (при GC или уменьшении нагрузки). Pool предназначен только для временного хранения между goroutine.
Гарантирует ли Pool возвращение именно того же объекта, который был ранее помещён?
Нет. Pool возвращает любой подходящий объект или создаёт новый при необходимости. Не следует хранить привязку между пользователем и объектом Pool.
Нужно ли очищать/reset-ить объект перед возвратом в Pool?
Да, иначе следующий поток может получить "грязный" объект с остатками предыдущих данных.
Негативный кейс
Разработчик сохраняет в Pool состояния клиентов для повторного подключения к чату. После запуска GC соединения исчезают, пользователи теряют данные.
Плюсы:
Минусы:
Позитивный кейс
Buffer-объекты для marshal/unmarshal JSON складывают в Pool и используют для batch обработки сообщений. После обработки объекты очищаются и возвращаются в пул, снижая количество аллокаций.
Плюсы:
Минусы: