Вольный пересказ документации к sync.Pool
Сборщик мусора (далее GC) не постоянно собирает мусор, а через определённые промежутки времени. В случае если ваш код выделяет память под некоторые структуры данных, а потом освобождает их — и так по кругу — это вызывает определённое давление на GC, в том числе заставляет
Что же делать?
Функция
Гораздо удобней сделать две функции, которые бы занимались всей вознёй с пулом
Хороший пример использования пула: пакет fmt. Со 109-ой по 150-ую строку.
      	
		
	
    
      			  
              
            Сборщик мусора (далее GC) не постоянно собирает мусор, а через определённые промежутки времени. В случае если ваш код выделяет память под некоторые структуры данных, а потом освобождает их — и так по кругу — это вызывает определённое давление на GC, в том числе заставляет
runtime обратиться к ОС для выделения новой памяти. Представьте: выделяем кусок (например []byte), работаем с ним, освобождаем. Пройдёт определённое время, прежде GC "очнётся ото сна" и соберёт этот кусок. Если в это время мы выделим ещё один такой же кусок и уже выделенной у ОС памяти на это не хватит, то приложение будет вынуждено запросить у ОС ещё памяти. По времени приложения запрос памяти у ОС длится вечность. А в это самое время где-то пылится, ждёт своего часа тот прежний "отработанный" кусок.Что же делать?
- создать пул
 - сбрасывать состояние куска
 - складывать в пул отработанные куски
 - брать новые куски из пула
 
Создать пул
import(
    "sync"
)
var bytesPool = sync.Pool{
    New: func() interface{} { return []byte{} },
}
/*
В данном примере функция `New` не нужна. Если пул пуст,
и `New` не `nil` - то она будет использована для создания нового
объекта. Его нужно будет преобразовать из `interace{}` - привести
к нужному типу. Смотри ниже - про это есть децл.
*/Сбросить состояние
// пусть ary у нас []byte определённой длины и ёмкости
ary = ary[:0]
// усекаем len, сохраняем capПоложить в пул
/*
так или иначе у нас могут оказаться слишком большие куски,
которые в принципе нам не понадобятся (во всяком случае
не часто) - выбросим их; иначе: кусок размером 2048 байт
будет использоваться там где нужно всего 500-800 байт,
при большом количестве это негативно отразится на памяти
- а ведь мы с этим и боремся
*/
const maxCap = 1024
if cap(ary) <= maxCap {
    // кладём в пул куски ограниченного размера
    bytesPool.Put(ary)
}Взять из пула
nextAry := bytesPool.Get().([]byte)Пояснение про New
Функция
New создаёт пустой []byte{}, да ещё и эти преобразования в interface{} и обратно. В случае с []byte мы скорее всего будем наращивать его с помощью append, что в принципе делает такой подход не выгодным:- создание 
[]byteнулевой ёмкости - двойное преобразование в 
interface{}и обратно appendвсё равно создаст новый кусокappendможно скормитьnil, только типа []byte (а не interface{})
Гораздо удобней сделать две функции, которые бы занимались всей вознёй с пулом
// получить
func getBytes() (b []byte) {
    ifc := bytesPool.Get()
    if ifc != nil {
        b = ifc.([]byte)
    }
    return
}
// положить
func putBytes(b []byte) {
    if cap(b) <= maxCap {
        b = b[:0] // сброс
        bytesPool.Put(b)
    }
}Помните
sync.Poolне панацея- пул горутино-безопасен
 - пул не обязательно освободит данные при первом пробуждении GC, но он может освободить их в любой момент
 - нет возможности определить и установить размер пула
 - нет необходимости заботится о переполнении пула
 - вовсе незачем городить пул везде где ни попади, он создавался как амортизатор при множественном совместном использовании некоторых общих объектов, даже не просто внутри пакета, а даже больше — другими пакетами
 - вероятно у Вас есть или будут ситуации, когда необходимость/возможность помочь GC будет очевидной
 - пул ограниченного размера делается с помощью канала с буфером
 
Хороший пример использования пула: пакет fmt. Со 109-ой по 150-ую строку.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.