# GC

Визуализация выглядит следующим образом:

![ ](https://russianblogs.com/images/21/7c3cd21ebfd42d828205688aea87ae1d.gif)

Корневые объекты в Go:&#x20;

Глобальные переменные

&#x20;Стеки выполняющихся горутин

Чтобы создать сборщик мусора для следующего десятилетия мы обратились к алгоритму, написанному несколько десятилетий назад. Новый сборщик мусора в Go является конкурентным (concurrent), трехцветным (tri-color), mark-sweep сборщиком, идея которого впервые была предложена [Дейкстрой в 1978 году](http://dl.acm.org/citation.cfm?id=359655). Этот подход намеренно так сильно несоответствует большинству современных сборщиков мусора «энтерпрайз»-уровня, и мы считаем, что это наилучший подход для современного железа и требований к паузам на новом железе.\
\
&#x20;В трехцветном сборщике мусора, каждый объект может быть помечен, как белый, серый или чёрный, и мы рассматриваем кучу (heap) как граф связанных объектов. В начале каждого цикла GC все объекты белые. GC проходит всё корневые узлы (roots) графа, которые являются объектами, напрямую доступными программе — это глобальные переменные и переменные в стеке — и помечает их серыми. Затем GC выбирает серый объект, делает его чёрным, а затем сканирует его на наличие указателей и других объектов. Если скан обнаруживает указатель на белый объект, он делает его серым. Этот процесс повторяется, пока не останется серых объектов. В этот момент все белые объекты будут считаться недостижимыми и могут быть переиспользованы.\
\
&#x20;Это всё происходит параллельно с работой программы, также называемой мутатором (mutator), изменяющей указатели по мере того, как работает сборщик. Из этого следует, что мутатор должен поддерживать инвариант того, что ни один черный объект не указывает на белый, чтобы сборщик мусора не потерял след объекта в той части кучи, которую он уже обошел. Поддержка такого инварианта это задача для «барьеров записи» (write barrier), которая по сути является маленькой функцией, запускающейся каждый раз, когда мутатор меняет указатель в куче. Барьер записи в Go помечает серым объекты, которые были белыми, гарантируя, что сборщик мусора рано или поздно просканирует его.\
\
&#x20;Определение момента, когда все объекты просканированы — тонкая задача и может быть очень дорогостоящей и сложной, если мы хотим избежать блокировок мутатора. Для простоты Go 1.5 делает максимум возможного в фоне, а потом приостанавливает программу на совсем короткое время, чтобы проверить все потенциально серые объекты. Найти идеальное соотношение для времени необходимого для этой паузы и для всей работы GC является одной из главных задач для Go 1.6.\
\
&#x20;Конечно же, дьявол кроется в деталях. Когда начинать очередной цикл GC? Какую метрику использовать для принятия этого решения? Как должны взаимодействовать GC и планировщик Go? Как останавливать потоки мутатора так, чтобы хватило времени на сканирование их стеков? Как мы представляем белые, серые и черные объекты, чтобы максимально эффективно искать и сканировать серые объекты? Как мы узнаём, где их корневые узлы? Как мы узнаём, где находятся указатели в объекте? Как мы минимизируем фрагментацию памяти? Как мы решаем вопросы производительности кешей? Насколько большой должна быть куча? И так далее, и тому подобное, кое-что относящееся к аллокациям, кое-что к поиску достижимых объектов, кое-что к планированию, но основные вопросы затрагивают производительность. Дискуссии о более низкоуровневых деталях каждой из этих областей выходят за рамки этого поста.\
\
&#x20;На более высоком уровне, один из подходов решения этих проблем для GC является добавление в GC ползунков (knobs), по одному на каждую задачу. Разработчик тогда может тюнить GC под себя, настраивая множество параметров. Минус тут в том, что через 10 лет с одним или двумя новыми ползунками каждый год, вы в итоге заканчиваете Трудовым Договором Об Использовании Переключателей GC. Go не пойдёт этим путём. Вместо этого мы даём лишь один ползунок, называемый GOGC. Его значение контролирует общий размер кучи относительно размера достижимых объектов. Дефолтное значение «100» означает, что общий размер кучи сейчас на 100% больше (тоесть, вдвое) размера реально достижимых объектов после последнего цикла GC. «200» означает, что общий размер кучи на 200% больше (тоесть, в три раза), чем размер реально используемых объектов. Если вы хотите уменьшить общее количество времени работы GC, увеличьте GOGC. Если вы хотите отдать больше времени GC, и выиграть себе память — уменьшайте GOGC.\
\
&#x20;Важно понимать, что по мере того, как количество памяти удвоится со следующим поколением железа, простое увеличение GOGC вдвое уменьшит количество циклов GC вдвое. С другой стороны, так как GOGC оперирует понятием достижимых объектов, увеличение нагрузки и сопутствующее увеличение количества достижимых объектов не нуждается в ретюнинге. Приложение просто масштабируется. Более того, необременённая поддержкой дюжин ползунков, команда, пишущая рантайм языка может сфокусироваться на улучшении рантайма, основываясь на фидбеке от реальных программ в продакшене.

![](https://1596906813-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MSIObV-s_nRzdVoJ5_8%2F-MWsxA_k8vk8A1C08mrP%2F-MWtHOFvaUj2vHqQPhn3%2F%D0%B8%D0%B7%D0%BE%D0%B1%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5.png?alt=media\&token=2fc869fc-118c-4c94-8015-a8c1437de84d)

![](https://1596906813-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MSIObV-s_nRzdVoJ5_8%2F-MWsxA_k8vk8A1C08mrP%2F-MWtHYGCvd7rfgu-OnrE%2F%D0%B8%D0%B7%D0%BE%D0%B1%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5.png?alt=media\&token=f0a7fce3-fde2-4484-9863-7f6623926d76)

**Периоды запуска GC**

* периодически из потока sysmon , если прошло достаточно много времени с последнего запуска
* после выделения памяти, если выделен большой Span или не удалось выделить быстро&#x20;
* при ручном вызове runtime.GC()
