- PVSM.RU - https://www.pvsm.ru -
Вольный пересказ документации к sync.Pool [1]
Сборщик мусора (далее 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
создаёт пустой []byte{}
, да ещё и эти преобразования в interface{}
и обратно. В случае с []byte
мы скорее всего будем наращивать его с помощью append
, что в принципе делает такой подход не выгодным:
[]byte
нулевой ёмкостиinterface{}
и обратноappend
всё равно создаст новый кусокappend
можно скормить nil
, только типа []byte (а не interface{}) [2]Гораздо удобней сделать две функции, которые бы занимались всей вознёй с пулом
// получить
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
не панацеяХороший пример использования пула: пакет fmt [4]. Со 109-ой [5] по 150-ую строку.
Автор: deep_orange
Источник [6]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/memory-management/112157
Ссылки в тексте:
[1] sync.Pool: https://godoc.org/sync#Pool
[2] только типа []byte (а не interface{}): http://play.golang.org/p/P6ZqGE91a2
[3] канала с буфером: http://play.golang.org/p/c7DtgZtq0E
[4] fmt: https://godoc.org/fmt
[5] 109-ой: https://golang.org/src/fmt/print.go#L109
[6] Источник: https://habrahabr.ru/post/277137/
Нажмите здесь для печати.