Как свести к минимуму сбор мусора в Go?
Несколько раз вы могли бы захотеть избежать/свести к минимуму сборщик мусора, поэтому я хочу быть уверенным, как это сделать.
Я думаю, что следующий правильный:
- Объявить переменные в начале функции.
- Использовать массив вместо среза.
Больше?
Ответы
Ответ 1
Избегать мусора относительно прямо. Вам нужно понять, где делаются ассигнования, и посмотреть, можно ли избежать выделения.
Во-первых, объявление переменных в начале функции НЕ поможет. Компилятор не знает разницы. Тем не менее, человек будет знать разницу, и это будет раздражать их.
Использование массива вместо среза будет работать, но это связано с тем, что в стек помещаются массивы (если не разыменовываются). Массивы имеют другие проблемы, такие как тот факт, что они передаются по значению (копируются) между функциями. Все, что в стеке, "не мусор", поскольку оно будет освобождено при возврате функции. Любой указатель или срез, который может выйти из функции, помещается в кучу, с которой сборщик мусора должен иметь дело в какой-то момент.
Лучшее, что вы можете сделать, это избежать выделения. Когда вы закончите с большими битами данных, которые вам не нужны, используйте их повторно. Это метод, используемый в профилировании в блоге Go. Я предлагаю прочитать его.
Другой пример, кроме примера в учебнике профилирования: Допустим, у вас есть фрагмент типа []int
с именем xs
. Вы постоянно добавляете к []int
, пока не достигнете условия, а затем вы reset, чтобы вы могли начать все заново. Если вы выполняете xs = nil
, вы теперь объявляете базовый массив среза как сборку мусора. Затем Append перераспределяет xs при следующем использовании. Если вместо этого вы выполняете xs = xs[:0]
, вы все равно перезагружаете его, но сохраняете старый массив.
По большей части, попытка избежать мусора - преждевременная оптимизация. Для большей части вашего кода это не имеет значения. Но вы можете найти время от времени функцию, которая называется много раз, которая выделяет много при каждом запуске. Или цикл, в котором вы перераспределяете вместо повторного использования. Я подожду, пока ты не увидишь горло бутылки перед тем, как отправиться за борт.
Ответ 2
Чтобы минимизировать сбор мусора в Go, вы должны минимизировать распределение кучи. Чтобы минимизировать распределение кучи, вы должны понимать, когда происходит распределение.
Следующие вещи всегда вызывают распределения (по крайней мере, в компиляторе gc по адресу Go 1):
- Использование встроенной функции
new
- Использование встроенной функции
make
(за исключением нескольких маловероятных угловых случаев)
- Композитные литералы, когда тип значения представляет собой срез, карту или структуру с оператором
&
- Вставка значения, превышающего машинное слово, в интерфейс. (Например, строки, срезы и некоторые структуры больше машинного слова.)
- Преобразование между
string
, []byte
и []rune
- Начиная с Go 1.3, в особых случаях компилятор это выражение не выделяет:
m[string(b)]
, где m
- это карта, а b
- это []byte
- Преобразование целочисленного значения без константы в
string
-
defer
-
go
- Функциональные литералы, которые фиксируют локальные переменные
В зависимости от деталей следующие причины могут вызывать распределения:
- Взятие адреса переменной. Обратите внимание, что адреса могут быть взяты неявно. Например,
a.b()
может принимать адрес a
, если a
не является указателем, а метод b
имеет тип приемника указателя.
- Использование встроенной функции
append
- Вызов вариационной функции или метода
- Нарезка массива
- Добавление элемента к карте
Список должен быть полным, и я в этом уверен, но с удовольствием рассмотрю дополнения или исправления.
Если вы не уверены в том, где происходят ваши распределения, вы всегда можете профилировать, как предложили другие, или посмотреть на сборку, создаваемую компилятором.