[インデックス 13428] ファイルの概要
このコミットでは、Goランタイムのメモリ管理に関連する複数のファイルが変更されています。主な変更点は、freemcache() という新しい関数の追加と、既存の purgecachedstats() 関数の引数変更です。
src/pkg/runtime/malloc.goc: メモリ割り当てのコアロジックが含まれるファイルで、purgecachedstatsの呼び出し元が更新され、freemcache関数が追加されています。src/pkg/runtime/malloc.h: メモリ割り当て関連の関数プロトタイプが定義されているヘッダーファイルで、freemcacheの宣言とpurgecachedstatsの引数変更が反映されています。src/pkg/runtime/mgc0.c: ガベージコレクション (GC) の統計処理に関連するファイルで、purgecachedstatsの呼び出し元が更新されています。src/pkg/runtime/mheap.c: グローバルヒープ (MHeap) の管理に関連するファイルで、purgecachedstatsの呼び出し元が更新されています。src/pkg/runtime/runtime.h: ランタイム全体の共通ヘッダーファイルで、freemcacheの関数プロトタイプが追加されています。
コミット
runtime: add freemcache() function
It will be required for scheduler that maintains
GOMAXPROCS MCache's.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/6350062
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/ed516df4e43c5e3467bd6a39ffc9277157574788
元コミット内容
commit ed516df4e43c5e3467bd6a39ffc9277157574788
Author: Dmitriy Vyukov <dvyukov@google.com>
Date: Sun Jul 1 13:10:01 2012 +0400
runtime: add freemcache() function
It will be required for scheduler that maintains
GOMAXPROCS MCache's.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/6350062
---
src/pkg/runtime/malloc.goc | 18 +++++++++++++-----\n src/pkg/runtime/malloc.h | 2 +-\n src/pkg/runtime/mgc0.c | 4 ++--\n src/pkg/runtime/mheap.c | 4 ++--\n src/pkg/runtime/runtime.h | 1 +\n 5 files changed, 19 insertions(+), 10 deletions(-)\n
変更の背景
このコミットの主な目的は、Goランタイムに freemcache() 関数を追加することです。コミットメッセージには「スケジューラが GOMAXPROCS の MCache を維持するために必要となる」と明記されています。
Goのランタイムスケジューラは、Goプログラムの並行実行を管理します。GOMAXPROCS は、同時に実行できるOSスレッドの最大数を制御する環境変数であり、Goランタイムが利用できる論理プロセッサ (P) の数に影響を与えます。各論理プロセッサ (P) には、スレッドローカルなメモリキャッシュである MCache が関連付けられています。
Goランタイムの進化の過程で、スケジューラは GOMAXPROCS の値の変更や、プロセッサ (P) の動的な割り当て・解放に応じて、関連する MCache のライフサイクルを適切に管理する必要が生じました。具体的には、不要になった MCache を安全かつ効率的に解放するメカニズムが求められていました。
freemcache() 関数は、この MCache の解放処理をカプセル化するために導入されました。これにより、スケジューラは必要に応じて MCache をクリーンアップし、メモリリソースを適切に管理できるようになります。また、purgecachedstats() 関数の引数が M* (OSスレッドを表す構造体) から MCache* へと変更されたのは、この統計パージ処理が MCache 自体に直接関連するものであり、より汎用的に利用できるようにするためのリファクタリングと考えられます。
前提知識の解説
このコミットを理解するためには、Goランタイムの以下の主要な概念を把握しておく必要があります。
1. Goランタイムの概要
Goランタイムは、Goプログラムの実行を管理するシステムです。これには、ガベージコレクション (GC)、スケジューラ、メモリ管理、プリミティブな同期メカニズムなどが含まれます。Goプログラムは、OSによって直接実行されるのではなく、Goランタイム上で動作します。
2. Goスケジューラ (M, P, Gモデル)
Goスケジューラは、Goの並行処理モデルの中核をなすものです。主に以下の3つの要素で構成されます。
- G (Goroutine): Goにおける軽量な実行単位です。OSスレッドよりもはるかに軽量で、数百万個作成することも可能です。
- M (Machine): OSスレッドを表します。Goランタイムは、Goコードを実行するためにOSスレッドをMとして利用します。Mは、Gを実行したり、システムコールを処理したりします。
- P (Processor): 論理プロセッサを表します。PはMとGの間の仲介役であり、Gを実行するためのコンテキストを提供します。各Pは、実行可能なGのローカルキューを持ち、MはPにアタッチされてGを実行します。
GOMAXPROCS環境変数は、同時に実行できるPの最大数を決定します。
このモデルにより、Goランタイムは少数のOSスレッド (M) で多数のGoroutine (G) を効率的に多重化し、並行処理を実現します。
3. Goのメモリ管理 (MCache, MHeap, FixAlloc)
Goランタイムは、独自のメモリ管理システムを持っています。
- MHeap (グローバルヒープ): Goプログラムが割り当てるすべてのオブジェクトのメモリが最終的に格納される場所です。MHeapは、大きなメモリチャンク (スパン) を管理し、必要に応じてMCacheに提供します。
- MCache (メモリキャッシュ): 各P (または古いバージョンではM) に関連付けられたスレッドローカルなメモリキャッシュです。Goプログラムが小さなオブジェクトを割り当てる際、まずMCacheからメモリを確保しようとします。これにより、グローバルなMHeapへのアクセスを減らし、ロックの競合を最小限に抑え、割り当てのパフォーマンスを向上させます。MCacheが枯渇すると、MHeapから新しいメモリを取得します。
- FixAlloc: Goランタイム内部のデータ構造 (例えば、MCache自体やMHeapのスパン記述子など) を割り当てるために使用される、固定サイズのオブジェクトアロケータです。これは、特定のサイズのオブジェクトを効率的に割り当てるために設計されています。
4. purgecachedstats の役割
purgecachedstats 関数は、MCacheに蓄積されたメモリ割り当て統計情報 (例えば、割り当てられたオブジェクトの数やバイト数) を、グローバルなMHeapの統計情報にフラッシュする役割を担います。MCacheはスレッドローカルなため、正確な全体像を得るためには、定期的にその統計情報をグローバルな統計に統合する必要があります。これは、ガベージコレクションの判断やメモリプロファイリングの精度に影響します。
技術的詳細
このコミットにおける技術的な変更点は以下の通りです。
1. freemcache(MCache *c) 関数の導入
- 目的: 不要になった
MCacheオブジェクトを適切に解放し、関連するリソースをクリーンアップするために導入されました。これは、スケジューラがGOMAXPROCSの変更やプロセッサの動的な管理に伴い、MCacheのライフサイクルを制御する必要があるためです。 - 処理内容:
runtime·MCache_ReleaseAll(c):MCache内に残っているすべてのメモリブロックをグローバルヒープに解放します。これにより、MCacheが保持していたメモリが再利用可能になります。runtime·lock(&runtime·mheap): グローバルヒープのロックを取得します。これは、ヒープ統計の更新やFixAllocからのメモリ解放が競合しないようにするためです。runtime·purgecachedstats(c):MCacheの統計情報をグローバルな統計にフラッシュします。これにより、解放されるMCacheの統計が失われることなく、全体のメモリ使用状況に反映されます。runtime·FixAlloc_Free(&runtime·mheap.cachealloc, c):FixAllocを使用して割り当てられたMCacheオブジェクト自体を解放します。runtime·mheap.cacheallocは、MCacheオブジェクトを管理するためのFixAllocインスタンスです。runtime·unlock(&runtime·mheap): グローバルヒープのロックを解放します。
2. purgecachedstats 関数の引数変更
- 変更前:
void runtime·purgecachedstats(M* m) - 変更後:
void runtime·purgecachedstats(MCache* c) - 理由と影響: 以前は
M(OSスレッド) オブジェクトを引数に取り、そのMに関連付けられたMCacheを内部で参照していました。この変更により、purgecachedstatsは直接MCacheオブジェクトを引数として受け取るようになりました。これにより、関数がより汎用的になり、MCacheの統計をパージする操作がMのコンテキストから独立して呼び出せるようになります。これは、関数の責務を明確にし、モジュール性を高める良いリファクタリングです。この変更に伴い、malloc.goc,mgc0.c,mheap.c内のpurgecachedstatsの呼び出し箇所がすべて更新されています。
3. allocmcache における memclr の追加
runtime·allocmcache()関数内で、新しく割り当てられたMCacheオブジェクトcに対してruntime·memclr((byte*)c, sizeof(*c));が追加されました。- 目的: これは、新しく確保された
MCacheのメモリ領域をゼロクリアすることを保証します。これにより、以前のメモリ内容が残っていることによる潜在的なバグ (例えば、古いカウンタ値やポインタが残っていること) を防ぎ、MCacheが常にクリーンな状態で初期化されることを保証します。特に、カウンタやフラグなどの数値フィールドがゼロから始まることが期待される場合に重要です。
コアとなるコードの変更箇所
src/pkg/runtime/malloc.goc における freemcache の追加
--- a/src/pkg/runtime/malloc.goc
+++ b/src/pkg/runtime/malloc.goc
@@ -246,12 +247,19 @@ runtime·allocmcache(void)
}
void
-runtime·purgecachedstats(M* m)
+runtime·freemcache(MCache *c)
{
-\tMCache *c;\n+\truntime·MCache_ReleaseAll(c);\n+\truntime·lock(&runtime·mheap);\n+\truntime·purgecachedstats(c);\n+\truntime·FixAlloc_Free(&runtime·mheap.cachealloc, c);\n+\truntime·unlock(&runtime·mheap);\n+}\n \n+void\n+runtime·purgecachedstats(MCache *c)
+{\n \t// Protected by either heap or GC lock.\n-\tc = m->mcache;\n \tmstats.heap_alloc += c->local_cachealloc;\n \tc->local_cachealloc = 0;\n \tmstats.heap_objects += c->local_objects;\
src/pkg/runtime/malloc.h における purgecachedstats の引数変更と freemcache の宣言
--- a/src/pkg/runtime/malloc.h
+++ b/src/pkg/runtime/malloc.h
@@ -401,7 +401,7 @@ void runtime·markspan(void *v, uintptr size, uintptr n, bool leftover);\n void runtime·unmarkspan(void *v, uintptr size);\n bool runtime·blockspecial(void*);\n void runtime·setblockspecial(void*, bool);\n-void runtime·purgecachedstats(M*);\n+void runtime·purgecachedstats(MCache*);\n
enum
{
src/pkg/runtime/runtime.h における freemcache の宣言
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -590,6 +590,7 @@ int32 runtime·funcline(Func*, uintptr);\n void* runtime·stackalloc(uint32);\n void runtime·stackfree(void*, uintptr);\n MCache* runtime·allocmcache(void);\n+void runtime·freemcache(MCache*);\n void runtime·mallocinit(void);\n bool runtime·ifaceeq_c(Iface, Iface);\n bool runtime·efaceeq_c(Eface, Eface);\
コアとなるコードの解説
runtime·freemcache(MCache *c)
この関数は、引数として渡された MCache オブジェクト c を解放する責任を持ちます。
runtime·MCache_ReleaseAll(c);:MCacheが保持しているすべてのメモリブロック (スパン) をグローバルヒープに返却します。これにより、これらのメモリは他の割り当てのために再利用可能になります。runtime·lock(&runtime·mheap);とruntime·unlock(&runtime·mheap);: グローバルヒープ (runtime·mheap) へのアクセスを保護するためのロックです。purgecachedstatsの呼び出しとFixAlloc_Freeによるメモリ解放がアトミックに行われることを保証します。runtime·purgecachedstats(c);: 解放されるMCacheのローカル統計情報をグローバルな統計にフラッシュします。これにより、このMCacheが使用していた期間の正確な統計がシステム全体に反映されます。runtime·FixAlloc_Free(&runtime·mheap.cachealloc, c);:MCacheオブジェクト自体がFixAllocを使って割り当てられているため、そのFixAllocインスタンス (runtime·mheap.cachealloc) を通じてMCacheオブジェクトのメモリを解放します。
runtime·purgecachedstats の引数変更
runtime·purgecachedstats の関数シグネチャが M* から MCache* に変更されました。
- 変更前は、
M(OSスレッド) オブジェクトからm->mcacheを介してMCacheを取得していました。 - 変更後は、直接
MCacheオブジェクトを受け取るため、この関数はMCacheの統計をパージするという単一の責務に集中できるようになりました。これにより、コードの可読性と再利用性が向上します。
runtime·allocmcache における memclr の追加
runtime·allocmcache 関数内で、新しく割り当てられた MCache オブジェクト c のメモリ領域全体が runtime·memclr((byte*)c, sizeof(*c)); によってゼロクリアされます。これは、確保されたメモリが不定な値を含まないことを保証し、特にカウンタやポインタなどのフィールドが初期状態でゼロであることを期待するGoランタイムの内部ロジックにとって重要です。これにより、潜在的なバグや予期せぬ動作を防ぎます。
関連リンク
- Go CL (Code Review) リンク: https://golang.org/cl/6350062
参考にした情報源リンク
- Goの公式ドキュメントやソースコード (Goランタイムの内部構造に関する一般的な知識)
- Goのメモリ管理とスケジューラに関する一般的な技術記事やブログポスト (MCache, MHeap, M-P-Gモデルの概念理解のため)