D E 全オブジェクトが未マーク状態 Root → A, B / A → E / B → C D はどこからも参照されていない ▶ ② Mark フェーズ ルートセット A B C D E 処理内容: 1. ルートセットからスタート 2. 参照を辿って到達可能なオブジェクトを探索 3. 発見したオブジェクトにマークを付ける ▶ ③ Sweep フェーズ ルートセット A B C FREE E 処理内容: 1. ヒープ全体をスキャン 2. マークがないオブジェクトを特定 3. メモリを解放してフリーリストに追加 ✅ メリット 実装がシンプルで理解しやすい 循環参照を正しく検出・解放できる メモリ使用量の予測が比較的容易 全てのガベージを確実に回収 ⚠ デメリット Stop-the-World(全スレッド停止)が必要 メモリの断片化(フラグメンテーション)が発生 ヒープが大きいと処理時間が長くなる キャッシュ効率が低下する可能性 未マーク マーク済み(生存) 解放済み(FREE)
(Gray) 探索中・参照先未確認 黒 (Black) 探索完了・生存確定 回収 白のまま→解放 Step 1: 初期状態 Root A B C D E 全オブジェクトが白(未探索状態) Step 2: ルートから開始 Root A B C D E ルートから到達可能な A, B を灰色に Step 3: 灰色を処理 Root A B C D E A→E, B→Cの参照先を灰色に / A, B自身を黒に変更 Step 4: 完了 Root A B C FREE E 灰色なし → マーキング完了 白が残っていれば回収対象 🔄 並行実行の 仕組み • Mutator(アプリ)とGCが並行動作 • Write Barrierで参照変更を追跡 • STWは最小限に抑制 • Go 1.5以降で採用 ⚡ Go 言語の 最適化 • Goroutine統合GCスケジューリング • ペーシング: メモリ基準でGC起動 • アシスト: MutatorがGC支援 • 目標: GC停止時間を10ms以下に ✅ 特徴とトレードオフ 利点: レイテンシ低、リアルタイム向き コスト: Write Barrierオーバーヘッド リソース: CPUコア1つをGC専用に メモリ: フットプリントやや大 📝 Write Barrier (書き込みバリア)の役割 並行GC中にアプリがポインタを書き換えた場合、GCが見落とさないように通知する仕組み 例: 黒オブジェクトAが新たに白オブジェクトFを参照 → Write BarrierがFを灰色に変更し、GCの探索対象に追加 これにより、並行実行中でもオブジェクトの見落とし(Lost Update 問題)を防止する ※ 参照関係: Root → A, B / A → E / B → C / D はどこからも参照されていない(到達不能)
A ① ④ Page B ⑤ Page C ② ⑥ Page D ③ Page E Page F ⑦ Page G ⑧ Page H ❌ 問題点: • オブジェクトを ランダムにアクセス • メモリ全体を飛び回る(①→②→③... 異なるPage間) • キャッシュミスが頻発(35%のCPU サイクルを浪費) • 同じPageに何度も戻るが既にキャッシュから追い出されている • GCのCPU 時間の 大部分がキャッシュ待ちで浪費 🍵 Green Tea GC (ページベース) Page A ✓ 1 2 1 Page B ⏳ 3 4 5 2 Page C 6 7 3 Page D 8 4 Page E Page F 9 Page G 10 Page H ✅ 改善点: • Page単位で連続してスキャン( 局所性を最大化) • メモリ局所性が高い(8KiB 境界でアライメント) • GCオーバーヘッド10 40% 削減(GC多用プログラム) • バッチ効果: Page内の複数オブジェクトをまとめて処理 • 新しいamd64 CPU (Ice Lake/Zen 4 でさらに約10% 改善 白(未探索) 灰(探索中) 黒(完了) スキャン済みPage 処理中Page ランダムアクセス N スキャン順序