Ответ
sync.Pool
— это кеш временных объектов, предназначенный для повторного использования. Его основная цель — снизить нагрузку на сборщик мусора (GC) за счет уменьшения количества аллокаций памяти для короткоживущих объектов.
Принцип работы:
Get()
: Запрашивает объект из пула. Если пул пуст, вызывается функцияNew
(если она определена) для создания нового объекта.Put()
: Возвращает объект обратно в пул, делая его доступным для повторного использования.- Очистка:
sync.Pool
не является постоянным хранилищем. Объекты могут быть удалены из пула в любой момент во время сборки мусора. Начиная с Go 1.13, используется механизм с двумя кешами (локальным и "жертвенным"), что позволяет некоторым объектам пережить один цикл GC, но гарантий хранения все равно нет.
Ключевые правила и особенности:
- Потокобезопасность: Пул можно безопасно использовать из нескольких горутин одновременно.
- Нет гарантий: Нельзя рассчитывать, что объект, полученный через
Get()
, будет тем же самым, что был положен черезPut()
. Также нет гарантии, что послеGet()
вы не получитеnil
, если функцияNew
не задана. - Очистка состояния: Перед возвращением объекта в пул через
Put()
необходимо сбрасывать его состояние (например, обнулять слайсbuf = buf[:0]
), чтобы предыдущие данные не "протекли" в следующее использование.
Пример: переиспользование буфера []byte
var bufferPool = sync.Pool{
New: func() interface{} {
// Создаем буфер с запасом емкости
b := make([]byte, 0, 1024)
return &b
},
}
func processRequest(data []byte) {
// Берем буфер из пула
bufPtr := bufferPool.Get().(*[]byte)
defer func() {
// Очищаем буфер перед возвратом в пул
*bufPtr = (*bufPtr)[:0]
bufferPool.Put(bufPtr)
}()
// ... какая-то работа с буфером ...
*bufPtr = append(*bufPtr, data...)
}
Когда использовать:
- Высокочастотные аллокации: Когда в "горячем" участке кода постоянно создаются и удаляются одинаковые объекты (например, буферы, структуры для кодирования/декодирования).
- Тяжелые объекты: Для объектов, инициализация которых является дорогой операцией.
Когда НЕ использовать:
- Для долгосрочного кеширования (используйте map с mutex или
sync.Map
). - Для объектов, требующих явного закрытия (например,
*os.File
, сетевые соединения), так как нет гарантии, что методClose()
будет вызван.