Используете ли вы пакет sync.Pool для контроля памяти в Go
Что проверяют
Понимаете ли вы, что sync.Pool снижает нагрузку на сборщик мусора, а не «управляет памятью», и что пул ничего не обещает хранить. Следом спрашивают, как вы доказали, что пул помог, и что будет, если забыть сбросить объект.
Как отвечать
Отвечайте от опыта: какой горячий путь, какие объекты (bytes.Buffer для сериализации, временные структуры парсера), что показал профиль до и после. Цифры только свои. Если пул в работе не применяли, скажите прямо и объясните, при каком профиле до него дошли бы: пул нужен редко, и это нормальный ответ.
Устройство и ограничения:
- назначение: переиспользование короткоживущих объектов, которые в горячем пути создаются тысячами.
Get()отдаёт объект из пула или вызываетNew,Put()возвращает его; - нет гарантий: объект может исчезнуть в любой момент. Пул чистится сборщиком мусора, с Go 1.13 объекты переживают одну сборку через victim cache, но не больше. Соединения и всё с долгим состоянием в нём не держат;
- внутри: у каждого P свой локальный кэш, поэтому
GetиPutпочти всегда обходятся без блокировок; - примеры:
fmt,encoding/json,net/httpдержат в пулах свои буферы.
Сначала замер: go test -bench . -benchmem, профиль аллокаций в pprof, GODEBUG=gctrace=1 для частоты сборок. Часто дешевле убрать аллокацию совсем или настроить GOGC и GOMEMLIMIT.
Где спотыкаются
Не сбрасывают объект перед Put. Следующий Get получает буфер со старыми данными, иногда от чужого запроса. Нужен buf.Reset() или обнуление полей. Туда же относится работа с объектом после Put: это гонка данных.
Кладут в пул []byte по значению. Преобразование слайса в any само аллоцирует, и выигрыш пропадает. Кладут указатель *[]byte, об этом предупреждает staticcheck (SA6002).
Возвращают в пул огромные буферы после редкого большого запроса, и память остаётся раздутой, пока пул их крутит. fmt не возвращает в пул буферы крупнее 64 КБ, так же стоит делать и в своём коде.