Ответ
sync.Pool
— это пул для временных объектов, который помогает снизить нагрузку на сборщик мусора (GC) за счёт повторного использования уже выделенной памяти.
Как это работает?
Вместо того чтобы создавать новый объект при каждой необходимости (например, в цикле или при каждом HTTP-запросе), вы сначала пытаетесь получить его из пула (Get()
). Если пул пуст, создается новый объект. После использования вы возвращаете объект обратно в пул (Put()
), делая его доступным для повторного использования.
Это позволяет избежать частых операций выделения и освобождения памяти, которые нагружают GC.
Ключевые особенности:
- Потокобезопасность:
sync.Pool
можно безопасно использовать в нескольких горутинах одновременно. - Не для постоянного хранения: Объекты в пуле могут быть удалены сборщиком мусора в любой момент без предупреждения.
sync.Pool
не является кешем или хранилищем. - Поле
New
: Если при вызовеGet()
в пуле нет доступных объектов, будет вызвана функцияNew
(если она определена) для создания нового экземпляра.
Когда использовать?
- Когда в "горячих" участках кода часто создаются и уничтожаются однотипные, "тяжелые" объекты (например, буферы
bytes.Buffer
, большие структуры). - Для временных объектов в рамках одной операции (например, в HTTP-обработчиках для буферов кодирования/декодирования JSON).
Когда НЕ использовать?
- Для объектов, которые должны сохранять состояние между использованиями (например, сетевые соединения, подключения к БД). Пул может вернуть вам "грязный" объект или вообще его не вернуть.
Пример с bytes.Buffer
:
// Создаем пул для буферов
var bufPool = sync.Pool{
// New будет вызван, если Get() не найдет объект в пуле
New: func() any {
return new(bytes.Buffer)
},
}
func processRequest(data []byte) {
// 1. Получаем буфер из пула
buf := bufPool.Get().(*bytes.Buffer)
// 2. Важно! Возвращаем буфер в пул после использования
defer bufPool.Put(buf)
// 3. Очищаем буфер перед новым использованием
buf.Reset()
// Работаем с буфером
buf.Write(data)
// ...
}