Всім привіт, шановні колеги! Сьогодні я хотів би поговорити про “легені” Golang - його збирач сміття (GC). Тема, звичайно, давно відома і заїжджена, але оскільки я вже взяв за мету поступово розбирати найважливіші складові мови, то ніяк не міг би пропустити її.
Вступ
Garbage Collector (GC) в Go часто називають "неідеальним" через його відносну простоту, в порівнянні з більш складними реалізаціями в інших мовах програмування, але це зовсім не означає його неефективність для реальних застосунків.
Навпаки, команда розробників мови зробила дуже виважений та обґрунтований вибір на користь простоти реалізації та передбачуваності поведінки, свідомо жертвуючи деякими теоретично можливими оптимізаціями заради створення більш надійної та зрозумілої системи керування пам'яттю.
Як працює GC в Go?
Golang використовує паралельний, трикольоровий Mark-and-Sweep алгоритм. Давайте розберемо більш детально принцип його роботи. Як зрозуміло з назви, цей алгоритм має 2 фази роботи:
- Mark Phase - GC позначає всі досяжні об'єкти, використовуючи три кольори: білий, сірий і чорний:
- Першим етапом першої фази є початкове маркування, під час якого відбувається дуже коротка пауза зупинки світу (STW - Stop The World), щоб ідентифікувати всі кореневі об'єкти (наприклад, глобальні змінні, змінні стеку). Ці корені позначаються сірим кольором.
- Другим етапом GC конкурентно обходить граф об’єктів, позначаючи доступні об'єкти сірим і ставлячи їх у чергу для сканування. Після сканування всіх дочірніх елементів об'єкта він стає чорним. Об'єкти, які недоступні, залишаються білими. Цей етап виконується конкурентно з кодом застосунку, мінімізуючи збої.
- Останнім етапом фази є завершення маркування, що також супроводжується коротким STW для того, щоб переконатись, що маркування завершено.
- Sweep Phase - видалення всіх білих об'єктів, які залишились після маркування. Ця операція виконується конкурентно із роботою застосунка.
Абстракції та концепції
- Білий (White) - об'єкти, які ще не були відвідані (потенційне сміття).
- Сірий (Gray) - об'єкти, які були відвідані, але їхні дочірні елементи ще не були проскановані.
- Чорний (Black) - об'єкти, які були відвідані, і всі їхні дочірні елементи були проскановані. Вони вважаються живими.
- Захисні бар'єри (Write Barriers) - це невеликі фрагменти коду, які вставляються компілятором Go під час операцій запису вказівників. Вони допомагають GC відстежувати зміни в графі об'єктів під час конкурентного маркування, гарантуючи, що жодні живі об'єкти не будуть помилково позначені як білі і не будуть очищені.
Stop The World (STW) паузи
Сучасний GC в Go має дуже короткі STW паузи - в середньому <0.5ms
для більшості програм. Це досягається завдяки:
- Паралельному маркуванню (concurrent marking)
- Інкрементальному скануванню стеку
- Write barrier, що дозволяє програмі продовжувати роботу під час “збирання сміття”
Порівняння з іншими мовами
Java (JVM GC): JVM пропонує широкий спектр GC (G1, CMS, Parallel, ZGC, Shenandoah), які часто є набагато складнішими та конфігурованішими. Деякі з них також прагнуть до низької затримки (ZGC, Shenandoah), але зазвичай потребують більше уваги до налаштування. Go GC є простішим за дизайном і менш конфігурованим, з акцентом на "роботу з коробки". Java GC може призводити до довших пауз STW у певних сценаріях, хоча сучасні реалізації значно покращилися.