
2026/02/24 7:18
**Goランタイムの理解:メモリ割り当て器**
RSS: https://news.ycombinator.com/rss
要約▶
日本語訳:
要約:
Goランタイムのヒープアロケータは、アリーナ、ページ、およびスパンを使用して、ゴルーチンスタックやその他のオブジェクトのメモリを管理します。アリーナとは、
mmap(64‑ビットシステムでは約64 MB、Windows/32‑bitでは4 MB、WebAssemblyでは512 KB)で取得される大きな仮想アドレス空間です。アリーナ内では8 KBのページが基本的な割り当て単位となり、1 つ以上の連続したページが スパン を形成します。スパンは単一サイズクラスのオブジェクト専用に割り当てられます。各スパンにはフリーか使用中かを示す allocBits ビットマップと、ガベージコレクタによるライブオブジェクト追跡用の gcmarkBits ビットマップがあり、再利用時に遅延で掃除(スイープ)が行われます。
Go は 68 のサイズクラス(8 B–32 KB)を定義しており、それぞれに scan と noscan バリアントがあるため、合計 136 のスパンクラスになります。16 バイト未満のポインタを持たない極小オブジェクトは、複数のオブジェクトを単一の 16 バイトブロックに詰め込む特別な tiny アロケータで処理され、無駄が減少します。
割り当ては三段階の階層構造に従います:
- per‑P
(ロックフリー高速パス)mcache - per‑span‑class
(短時間ロック、中程度のパス)mcentral - global
(グローバルロック、低速パス)mheap
小規模な割り当てはローカルの
mcache から提供され、大きめのオブジェクト(最大32 KB)はまず mcentral を経由し、最後に mheap に到達します。32 KBを超えるオブジェクトは両方のキャッシュをバイパスして直接 mheap から割り当てられます。
バックグラウンドのスクレイジャー(scavenger)は定期的に未使用ページを OS に返却します(例:
MADV_DONTNEED を介し)が、プロセスアドレス空間内ではマップされたままです。この設計はメモリ使用量を効率化し、GC の停止時間を短く保つことで、高性能な並列アプリケーションを構築する Go 開発者にメリットを提供します。本文
Goランタイムのメモリ割り当て器 – 速習まとめ
割り当て器は、ヒープメモリを確保しガベージコレクタ(GC)と緊密に連携するランタイムの一部です。以下では、その設計と主要概念を簡潔に整理しています。
1. Go はデータをどこに置くか?
| エリア | 用途 | 生存期間 |
|---|---|---|
| Stack | 関数内ローカル変数 | 自動的に関数終了時に解放 |
| Heap | 生成した関数が終わった後も残る必要があるデータ | 長期的に生存し、割り当て器+GC が管理 |
コンパイラは escape analysis を実行して、値がスタック上に留まるかヒープへ逃げるかを決定します。
2. Go は頻繁に OS コールを避ける理由
やmmap
等のシステムコールはコストが高い。VirtualAlloc- ランタイムは 大きなブロック(アリーナ) を事前確保し、そこからメモリを割り当てます。
- アリーナが枯渇したときだけ OS へ新しいアリーナの要求を行います。
3. アリーナ内のメモリレイアウト
| レベル | サイズ | 役割 |
|---|---|---|
| Arena | 64 MB(典型的な 64‑bit) | OS から取得した大きなブロック |
| Page | 8 KB | アリーナ内での基本単位 |
| Span | 1 ページ以上、同一オブジェクトサイズ専用 | 固定長スロットを保持 |
| Slot | バイト | ユーザーへ返却される個別割り当て |
各 Span は二つのビットマップを持ちます:
– スロットが使用中かどうかallocBits
– GC が生きているオブジェクトをマークgcmarkBits
4. サイズクラスとスパン
| クラス | オブジェクトサイズ(バイト) | スパンページ数 | スパンあたりのオブジェクト数 |
|---|---|---|---|
| 1 | 8 | 1 | 1024 |
| … | … | … | … |
| 68 | 32 KB | 3–10 | 1–256 |
- オブジェクトは最も近いサイズクラスへ丸められます。
- scan と noscan フラグで各サイズクラスを分け、合計 136 の スパンクラス が存在します。
5. タイニーオブジェクト
- 16 バイト未満でポインタを含まないものは、単一の 16‑バイトブロックに詰められます。
- bool や int8 等の非常に小さい値でも無駄を最小化します。
6. 割り当て階層
| レベル | スコープ | ロック | 目的 |
|---|---|---|---|
| mcache | P(プロセッサ)ごと | 無し | ローカルスパンプールからロックフリーで高速に割り当て |
| mcentral | スパンクラスごと | 短時間 | mcache のスパンを補充、細粒度ロックを使用 |
| mheap | グローバル | 高コスト | ページ/アリーナが不足した際に新規確保 |
- 32 KB を超える大きなオブジェクトは
とmcache
をバイパスし、直接mcentral
に委ねられます。mheap
7. 割り当てフロー(簡易版)
- ゼロサイズ割り当て → グローバル
を返却zerobase - タイニー割り当て → 現在のタイニーブロックに詰めるか、mcache から新規取得
- 通常割り当て(16 B–32 KB)
- サイズを丸めてスパンクラスへ変換
- mcache を検索;空きスロットがあれば返却
- スパンが満杯なら
に移動し、新規スパンを要求mcentral
が空の場合はmcentral
からページ取得 → 新しいスパン作成mheap
- 大きな割り当て → 必要な正確なページ数を
で直接確保mheap
8. ガベージコレクタとの相互作用
- GC は
に生存オブジェクトをマークgcmarkBits - マーク後、割り当て器は
とallocBits
を入れ替え、未マークのスロットを解放gcmarkBits - スワッピング(スパンの再利用時に遅延で行われる)でスパンがクリアされます。
9. OS へメモリを返却
- scavenger ゴルーチンは定期的に未使用ページを OS に返却します (
を Linux 上で実行)。MADV_DONTNEED - ページはアドレス空間内にマップされたままですが、アイドル状態の物理メモリとして再利用可能です。
10. クイックリファレンス
| ファイル | 内容 |
|---|---|
| コア割り当てロジック() |
| アリーナとページ管理 |
| P ごとのスパンキャッシュ |
| スパンクラスごとのプール |
まとめ
Go の割り当て器は、事前に大きなアリーナを確保し、それをページ・スパンへ分割して管理します。ロックフリーのキャッシュを利用することで高速化を図りつつ、各スパンには二重ビットマップ(
allocBits と gcmarkBits)を持たせ GC と協調しています。最後に scavenger が未使用ページを OS に返却し、メモリの効率的な再利用を実現します。