
はじめに
エブリーでデリッシュキッチンの開発をしている本丸です。
1ヶ月前にGo Conference 2025があり色々と面白い発表があったのですが、その中にGo言語のガベージコレクションについての発表がありました。
ガベージコレクションについてやGo言語におけるガベージコレクションの動作について、学習したことがなかったため自分の知識を整理するという意味を込めてまとめられればと思います。
本記事の多くはGoのガベージコレクションの公式ドキュメントを参考にしているので合わせて確認いただければと思います。
ガベージコレクションの基礎
GCとは何か
ガベージコレクション(GC)とは、プログラムが動的に割り当てたメモリの中で、プログラムが将来アクセスしないと判断された領域(ガベージ)を自動的に検出し、解放して再利用するメモリ管理の仕組みです。
GCの最大の利点は、プログラマが手動でのメモリ確保・解放を行う必要がなくなることです。これにより、メモリリークや二重解放などの深刻なメモリ関連バグを根本的に防止し、システムの安定性が向上します。
Goの値が格納される場所
GCの中身の話に移る前にGoの値が格納される場所について確認したいと思います。 Goの値は主にスタックとヒープの2つの場所に格納されるのですが、動的にメモリが割り当てられコンパイラから解放のタイミングを決定できないヒープ領域がGCの対象となります。
- スタック: GC管理対象外、コンパイラが解放タイミングを決定
- ヒープ: GC管理対象、動的メモリ割り当ての場所
GCの種類
GCにはいくつか種類があるのですが、Go言語ではマーク・スイープ方式を採用しています。
| 方式 | 動作の概要 | メリット | デメリット | Go採用 |
|---|---|---|---|---|
| 参照カウント | 各オブジェクトに参照数を記録し、0で即時解放 | レイテンシが低い | 循環参照を検出できない | ❌ |
| マーク・スイープ | ルートからポインタを辿り、到達可能オブジェクトをマーク | 循環参照を処理可能 | 断片化が発生 | ✅ |
| コピーGC | 生存オブジェクトのみを別領域にコピーし、元の領域を解放 | 断片化なし | ヒープの半分しか利用不可 | ❌ |
Go言語のGCの動作原理
GCサイクル(3つのフェーズ)
Go言語のGCは主に以下の3つのフェーズに分かれており、3つのフェーズを繰り返すことで動作しています。
- スイープフェーズ: 以前のサイクルで生存オブジェクトとしてマークされていないメモリを、新しい割り当てのために利用可能にする
- オフ: GCが非アクティブな期間
- マークフェーズ: 生存オブジェクトを識別しマークする
マークフェーズ
3つのフェーズの中でもマークフェーズに関しては動作の仕組みで2つポイントがあるので押さえて置けたらと思います。
三色マーキングアルゴリズム
Go言語のGCでは三色マーキングアルゴリズムを採用し、すべてのオブジェクトを3つの色に分類し、その色によってスイープフェーズでメモリを解放するかを決定します。
| 色 | 状態 | 意味 | 処理 |
|---|---|---|---|
| 白 | 未到達 | まだスキャンされていない | 回収対象 |
| グレー | 到達済み・未スキャン | 到達したが子要素未確認 | スキャン待ち |
| 黒 | 到達済み・スキャン完了 | すべての子要素も確認済み | 生存確定 |
ライトバリア
GoのGCでは、アプリケーションの実行中にGCが動作するため、本来マークされなければならないオブジェクトがマークされない可能性があります。これを解決するためにライトバリアと呼ばれる仕組みを利用して、参照先を即座にグレーに変更することで、オブジェクトの見落としを防いでいます。
GreenTea Garbage Collector
最後にGreenTea Garbage Collector(GreenTea GC)について少しだけ触れておこうかと思います。 従来のGCのマークアルゴリズムだとオブジェクトの位置を考慮せず局所性が低いことがパフォーマンス上の問題になっていました。そこで1.25からGreenTea Garbage Collectorが実験的に導入されました。 ここでは、GreenTea Garbage Collectorの詳細には触れないため、気になった方は参考文献のIssueやスライドをご確認いただければと思います。
GreenTea GCでなにが変わったか
従来のGCでは局所性に問題があったため、GreenTea GCでは局所性の課題の解消を目標にしています。そのためにマークフェーズにおけるマークの単位をオブジェクトからスパン(オブジェクトを格納するメモリブロックの単位)で行うように変更されています。厳密にはスパン単位とオブジェクト単位のマークを組み合わせたり、スパンのスパースによって処理を分岐したりとスパン単位でのマークを追加したのに合わせて、効率よく処理が行われるように処理を分岐させているようです。
まとめ
今回はGo言語でのGCを中心にガベージコレクションについて学んだことをまとめてみました。ガベージコレクションがどんなことをするものなのか概要については知っていましたが、具体的な動き方については知らなかったのでこの機会に勉強できてよかったかと思います。 また、学習の中でGo言語のGCのランタイムを追ってみたのですが、実際のコードを追ってみることで内容を理解する助けにもなったので、機会を見つけて別のランタイムのコードを追ってみようかと思いました。