[インデックス 17020] ファイルの概要
このコミットは、Goランタイムのメモリ管理に関連するデッドコードの削除を目的としています。具体的には、型メタデータの割り当てにおいて SysAlloc を使用していた古い、もはや不要なコードパスが削除されました。
変更されたファイルは以下の通りです。
src/pkg/runtime/malloc.goc: メモリ割り当てと型情報フラッシュの主要なロジックが含まれるファイル。src/pkg/runtime/malloc.h: メモリ割り当てに関連するデータ構造と関数の宣言が含まれるヘッダーファイル。src/pkg/runtime/mgc0.c: ガベージコレクションの初期化と実行に関連するファイル。src/pkg/runtime/mheap.c: ヒープ管理に関連するファイル。
コミット
runtime: remove dead code
Remove dead code related to allocation of type metadata with SysAlloc.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/12311045
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/0a904a3f2ed1c4c09acdb9116aa3843e5ad14dad
元コミット内容
このコミットの元々の内容は、「runtime: remove dead code」(ランタイム: デッドコードの削除)であり、具体的には「Remove dead code related to allocation of type metadata with SysAlloc.」(SysAlloc を用いた型メタデータの割り当てに関連するデッドコードを削除)と説明されています。
変更の背景
このコミットの背景には、Goランタイムのメモリ管理戦略の進化があります。Goのガベージコレクタは、オブジェクトの型情報を利用してポインタを識別し、到達可能なオブジェクトを追跡します。この型メタデータは、ヒープ上に割り当てられたオブジェクトがどのような構造を持ち、どのフィールドがポインタであるかをGCに教えるために不可欠です。
以前のGoランタイムでは、この型メタデータを割り当てる際に、runtime·SysAlloc というシステムコールを直接利用するパスが存在していました。SysAlloc はOSから直接メモリを要求する低レベルな関数であり、Goランタイムのヒープ管理とは独立して動作します。
しかし、Goランタイムのメモリ管理が成熟し、特にガベージコレクタの改善が進むにつれて、型メタデータの割り当ても通常のヒープ割り当てメカニズム(runtime·mallocgc など)を通じて行われるようになりました。これにより、SysAlloc を直接使用するコードパスは不要となり、デッドコード(到達不能なコード、または実行されることがないコード)と化しました。
このコミットは、このような不要になった SysAlloc 関連のコードを削除することで、ランタイムのコードベースを整理し、保守性を向上させることを目的としています。デッドコードの削除は、コードの複雑性を減らし、将来の変更における潜在的なバグのリスクを低減する上で重要です。
前提知識の解説
このコミットを理解するためには、以下のGoランタイムの概念とメモリ管理の基礎知識が必要です。
-
Goランタイム (Go Runtime): Goプログラムの実行を管理する部分です。これには、ガベージコレクタ (GC)、スケジューラ (goroutineの管理)、メモリ割り当て、システムコールインターフェースなどが含まれます。Goプログラムは、OS上で直接実行されるのではなく、このランタイム上で動作します。
-
ガベージコレクション (Garbage Collection, GC): Goは自動メモリ管理を採用しており、開発者が手動でメモリを解放する必要はありません。GCは、プログラムがもはや参照しないメモリ領域(オブジェクト)を自動的に識別し、解放して再利用可能にするプロセスです。GoのGCは、並行マーク&スイープ方式を採用しており、プログラムの実行と並行して動作します。
-
型メタデータ (Type Metadata): GoのGCは、正確なGC(Precise GC)です。これは、GCがメモリ内のどの値がポインタであり、どの値がポインタでないかを正確に知っていることを意味します。この情報が「型メタデータ」です。各Goオブジェクトは、その型に関する情報(例えば、構造体のどのフィールドがポインタであるか、配列の要素がポインタであるかなど)をGCに提供します。これにより、GCはヒープを正確にスキャンし、到達可能なオブジェクトをマークできます。
-
SysAllocとSysFree:runtime·SysAllocは、GoランタイムがOSから直接メモリを要求するために使用する低レベルな関数です。これは、Goのヒープ管理システム(mheap)を介さずに、OSの仮想メモリ管理機能(例: Linuxのmmap、WindowsのVirtualAlloc)を直接利用します。runtime·SysFreeは、SysAllocで割り当てられたメモリをOSに返却するための対応する関数です。これらは、ランタイムの初期化や、非常に特殊なメモリ領域(例えば、スタックやGCメタデータの一部)の割り当てに使用されることがあります。 -
mallocgc:runtime·mallocgcは、Goランタイムの主要なメモリ割り当て関数です。これは、Goのヒープ(mheap)からメモリを割り当て、必要に応じてガベージコレクタをトリガーします。ほとんどのGoオブジェクトは、この関数を通じて割り当てられます。 -
MSpan: Goランタイムのメモリ管理において、MSpanは連続したページ(通常は8KB)のブロックを表すデータ構造です。ヒープはこれらのMSpanの集合として管理され、各MSpanは特定のサイズのオブジェクトを格納するために使用されます。MSpanには、そのスパンに割り当てられたオブジェクトの型情報(MTypes)も関連付けられます。 -
MTypes構造体:src/pkg/runtime/malloc.hで定義されているMTypes構造体は、MSpan内のオブジェクトの型情報を保持します。この構造体は、型情報の圧縮形式(compressionフィールド)と、実際の型データへのポインタ(dataフィールド)を含みます。このコミット以前は、sysallocというブール値のフィールドも持っており、dataフィールドがSysAllocによって割り当てられたものかどうかを示していました。 -
settype_flush関数:runtime·settype_flushは、MSpanに関連付けられた型情報を更新またはフラッシュする関数です。これは、MSpanが新しいオブジェクト型を格納するようになった場合や、型情報の表現形式が変更された場合(例えば、より効率的な圧縮形式に変換される場合)に呼び出されます。この関数内で、型情報を格納するためのメモリが割り当てられます。 -
settype_sysfree関数:runtime·settype_sysfreeは、MSpanに関連付けられた型情報がSysAllocによって割り当てられた場合に、そのメモリをSysFreeを使って解放するための関数でした。
技術的詳細
このコミットの技術的な詳細は、主に runtime·settype_flush 関数と MTypes 構造体からの sysalloc フィールドの削除に集約されます。
1. runtime·settype_flush 関数の変更:
-
引数の変更: 変更前:
runtime·settype_flush(M *mp, bool sysalloc)変更後:runtime·settype_flush(M *mp)sysallocというブール引数が削除されました。この引数は、型メタデータをSysAllocで割り当てるべきか、それとも通常のヒープ割り当て(mallocgc)で行うべきかを制御していました。この引数が不要になったのは、型メタデータの割り当てが常にmallocgcを通じて行われるようになったためです。 -
SysAlloc関連のコードパスの削除:runtime·settype_flushの内部では、MTypes_EmptyからMTypes_Bytesへ、またはMTypes_Wordsへ型情報が変換される際に、新しい型情報を格納するためのメモリを割り当てていました。 変更前は、sysalloc引数の値に基づいて、runtime·mallocgcを使用するか、runtime·SysAllocを使用するかの条件分岐がありました。このコミットでは、runtime·SysAllocを呼び出すパスが完全に削除され、常にruntime·mallocgcが使用されるようになりました。--- a/src/pkg/runtime/malloc.goc +++ b/src/pkg/runtime/malloc.goc @@ -563,14 +563,13 @@ runtime·persistentalloc(uintptr size, uintptr align) static Lock settype_lock; void -runtime·settype_flush(M *mp, bool sysalloc) +runtime·settype_flush(M *mp) { uintptr *buf, *endbuf; uintptr size, ofs, j, t; uintptr ntypes, nbytes2, nbytes3; uintptr *data2; byte *data3; - bool sysalloc3; void *v; uintptr typ, p; MSpan *s; @@ -605,20 +604,9 @@ runtime·settype_flush(M *mp, bool sysalloc) case MTypes_Empty: ntypes = (s->npages << PageShift) / size; nbytes3 = 8*sizeof(uintptr) + 1*ntypes; - - if(!sysalloc) { - data3 = runtime·mallocgc(nbytes3, 0, FlagNoProfiling|FlagNoPointers|FlagNoInvokeGC); - } else { - data3 = runtime·SysAlloc(nbytes3); - if(data3 == nil) - runtime·throw("runtime: cannot allocate memory"); - if(0) runtime·printf("settype(0->3): SysAlloc(%x) --> %p\n", (uint32)nbytes3, data3); - } - + data3 = runtime·mallocgc(nbytes3, 0, FlagNoProfiling|FlagNoPointers|FlagNoInvokeGC); s->types.compression = MTypes_Bytes; - s->types.sysalloc = sysalloc; s->types.data = (uintptr)data3; - ((uintptr*)data3)[1] = typ; data3[8*sizeof(uintptr) + ofs] = 1; break; @@ -643,20 +631,8 @@ runtime·settype_flush(M *mp, bool sysalloc) } else { ntypes = (s->npages << PageShift) / size; nbytes2 = ntypes * sizeof(uintptr); - - if(!sysalloc) { - data2 = runtime·mallocgc(nbytes2, 0, FlagNoProfiling|FlagNoPointers|FlagNoInvokeGC); - } else { - data2 = runtime·SysAlloc(nbytes2); - if(data2 == nil) - runtime·throw("runtime: cannot allocate memory"); - if(0) runtime·printf("settype.(3->2): SysAlloc(%x) --> %p\n", (uint32)nbytes2, data2); - } - - sysalloc3 = s->types.sysalloc; - + data2 = runtime·mallocgc(nbytes2, 0, FlagNoProfiling|FlagNoPointers|FlagNoInvokeGC); s->types.compression = MTypes_Words; - s->types.sysalloc = sysalloc; s->types.data = (uintptr)data2; // Move the contents of data3 to data2. Then deallocate data3. @@ -665,12 +641,6 @@ runtime·settype_flush(M *mp, bool sysalloc) t = ((uintptr*)data3)[t]; data2[j] = t; } - if(sysalloc3) { - nbytes3 = 8*sizeof(uintptr) + 1*ntypes; - if(0) runtime·printf("settype.(3->2): SysFree(%p,%x)\n", data3, (uint32)nbytes3); - runtime·SysFree(data3, nbytes3); - } - data2[ofs] = typ; } break;
2. MTypes 構造体からの sysalloc フィールドの削除:
-
src/pkg/runtime/malloc.h内のMTypes構造体からbool sysalloc;フィールドが削除されました。このフィールドは、MTypes.dataがSysAllocによって割り当てられたメモリを指しているかどうかを示すために使用されていましたが、SysAllocパスが削除されたため不要になりました。--- a/src/pkg/runtime/malloc.h +++ b/src/pkg/runtime/malloc.h @@ -334,7 +334,6 @@ enum struct MTypes { byte compression; // one of MTypes_* - bool sysalloc; // whether (void*)data is from runtime·SysAlloc uintptr data; };
3. runtime·settype_sysfree 関数の削除:
-
src/pkg/runtime/malloc.gocからruntime·settype_sysfree関数全体が削除されました。この関数は、MSpanの型情報がSysAllocで割り当てられた場合に、そのメモリを解放するために存在していましたが、SysAllocパスが削除されたため、この関数も不要になりました。--- a/src/pkg/runtime/malloc.goc +++ b/src/pkg/runtime/malloc.goc @@ -681,33 +651,6 @@ runtime·settype_flush(M *mp, bool sysalloc) mp->settype_bufsize = 0; } -void -runtime·settype_sysfree(MSpan *s) -{ - uintptr ntypes, nbytes; - - if(!s->types.sysalloc) - return; - - nbytes = (uintptr)-1; - - switch (s->types.compression) { - case MTypes_Words: - ntypes = (s->npages << PageShift) / s->elemsize; - nbytes = ntypes * sizeof(uintptr); - break; - case MTypes_Bytes: - ntypes = (s->npages << PageShift) / s->elemsize; - nbytes = 8*sizeof(uintptr) + 1*ntypes; - break; - } - - if(nbytes != (uintptr)-1) { - if(0) runtime·printf("settype: SysFree(%p,%x)\n", (void*)s->types.data, (uint32)nbytes); - runtime·SysFree((void*)s->types.data, nbytes); - } -} - uintptr runtime·gettype(void *v) {
4. 呼び出し箇所の変更:
src/pkg/runtime/mgc0.cとsrc/pkg/runtime/mheap.cでは、runtime·settype_flushの呼び出し箇所からfalse引数が削除されました。これは、settype_flushのシグネチャ変更に対応するものです。src/pkg/runtime/mheap.cでは、MSpanの解放時にs->types.sysallocをチェックし、runtime·settype_sysfree(s)を呼び出すコードが削除されました。これはsettype_sysfree関数自体が削除されたためです。
これらの変更は、Goランタイムが型メタデータの割り当てにおいて、SysAlloc を直接使用する特殊なケースを完全に排除し、一貫して mallocgc を使用するようになったことを示しています。これにより、メモリ管理ロジックが簡素化され、デッドコードが取り除かれました。
コアとなるコードの変更箇所
このコミットのコアとなるコードの変更箇所は、主に src/pkg/runtime/malloc.goc と src/pkg/runtime/malloc.h に集中しています。
src/pkg/runtime/malloc.goc:
-
runtime·settype_flush関数のシグネチャ変更 (行 563-564):--- a/src/pkg/runtime/malloc.goc +++ b/src/pkg/runtime/malloc.goc @@ -563,14 +563,13 @@ runtime·persistentalloc(uintptr size, uintptr align) static Lock settype_lock; void -runtime·settype_flush(M *mp, bool sysalloc) +runtime·settype_flush(M *mp) {bool sysalloc引数が削除されました。 -
runtime·settype_flush内のSysAlloc関連コードの削除 (行 605-615, 643-653):MTypes_EmptyからMTypes_Bytesへの変換時:--- a/src/pkg/runtime/malloc.goc +++ b/src/pkg/runtime/malloc.goc @@ -605,20 +604,9 @@ runtime·settype_flush(M *mp, bool sysalloc) case MTypes_Empty: ntypes = (s->npages << PageShift) / size; nbytes3 = 8*sizeof(uintptr) + 1*ntypes; - - if(!sysalloc) { - data3 = runtime·mallocgc(nbytes3, 0, FlagNoProfiling|FlagNoPointers|FlagNoInvokeGC); - } else { - data3 = runtime·SysAlloc(nbytes3); - if(data3 == nil) - runtime·throw("runtime: cannot allocate memory"); - if(0) runtime·printf("settype(0->3): SysAlloc(%x) --> %p\n", (uint32)nbytes3, data3); - } - + data3 = runtime·mallocgc(nbytes3, 0, FlagNoProfiling|FlagNoPointers|FlagNoInvokeGC); s->types.compression = MTypes_Bytes; - s->types.sysalloc = sysalloc; s->types.data = (uintptr)data3; - ((uintptr*)data3)[1] = typ; data3[8*sizeof(uintptr) + ofs] = 1; break;MTypes_BytesからMTypes_Wordsへの変換時:--- a/src/pkg/runtime/malloc.goc +++ b/src/pkg/runtime/malloc.goc @@ -643,20 +631,8 @@ runtime·settype_flush(M *mp, bool sysalloc) } else { ntypes = (s->npages << PageShift) / size; nbytes2 = ntypes * sizeof(uintptr); - - if(!sysalloc) { - data2 = runtime·mallocgc(nbytes2, 0, FlagNoProfiling|FlagNoPointers|FlagNoInvokeGC); - } else { - data2 = runtime·SysAlloc(nbytes2); - if(data2 == nil) - runtime·throw("runtime: cannot allocate memory"); - if(0) runtime·printf("settype.(3->2): SysAlloc(%x) --> %p\n", (uint32)nbytes2, data2); - } - - sysalloc3 = s->types.sysalloc; - + data2 = runtime·mallocgc(nbytes2, 0, FlagNoProfiling|FlagNoPointers|FlagNoInvokeGC); s->types.compression = MTypes_Words; - s->types.sysalloc = sysalloc; s->types.data = (uintptr)data2;SysAllocを使用する条件分岐と、s->types.sysallocの設定が削除され、常にruntime·mallocgcが使用されるようになりました。 -
runtime·settype_sysfree関数の削除 (行 681-713):--- a/src/pkg/runtime/malloc.goc +++ b/src/pkg/runtime/malloc.goc @@ -681,33 +651,6 @@ runtime·settype_flush(M *mp, bool sysalloc) mp->settype_bufsize = 0; } -void -runtime·settype_sysfree(MSpan *s) -{ - uintptr ntypes, nbytes; - - if(!s->types.sysalloc) - return; - - nbytes = (uintptr)-1; - - switch (s->types.compression) { - case MTypes_Words: - ntypes = (s->npages << PageShift) / s->elemsize; - nbytes = ntypes * sizeof(uintptr); - break; - case MTypes_Bytes: - ntypes = (s->npages << PageShift) / s->elemsize; - nbytes = 8*sizeof(uintptr) + 1*ntypes; - break; - } - - if(nbytes != (uintptr)-1) { - if(0) runtime·printf("settype: SysFree(%p,%x)\n", (void*)s->types.data, (uint32)nbytes); - runtime·SysFree((void*)s->types.data, nbytes); - } -} - uintptr runtime·gettype(void *v) {関数全体が削除されました。
src/pkg/runtime/malloc.h:
MTypes構造体からのsysallocフィールドの削除 (行 334-337):--- a/src/pkg/runtime/malloc.h +++ b/src/pkg/runtime/malloc.h @@ -334,7 +334,6 @@ enum struct MTypes { byte compression; // one of MTypes_* - bool sysalloc; // whether (void*)data is from runtime·SysAlloc uintptr data; };bool sysalloc;の行が削除されました。
これらの変更は、Goランタイムのメモリ管理における型メタデータの割り当てが、SysAlloc を介する特殊なケースから、一貫して通常のヒープ割り当てメカニズムに統合されたことを明確に示しています。
コアとなるコードの解説
このコミットのコアとなるコードの変更は、Goランタイムのメモリ管理における型メタデータの割り当て方法の根本的な簡素化と統一を意味します。
-
runtime·settype_flushからsysalloc引数とSysAllocパスの削除: 以前のruntime·settype_flush関数は、sysallocというブール引数を受け取っていました。この引数は、型メタデータを格納するためのメモリを、Goの通常のヒープ割り当てメカニズム(runtime·mallocgc)で取得するか、それともOSから直接メモリを要求する低レベルなruntime·SysAllocで取得するかを決定していました。 このコミットでは、sysalloc引数が削除され、関数内部のSysAllocを呼び出す条件分岐も削除されました。これにより、settype_flushは常にruntime·mallocgcを使用して型メタデータ用のメモリを割り当てるようになりました。 これは、Goランタイムのメモリ管理が十分に成熟し、型メタデータのような内部的なデータ構造の割り当ても、通常のヒープ管理の枠組みの中で効率的に行えるようになったことを示唆しています。SysAllocのような低レベルなシステムコールを避けることで、メモリの断片化を減らし、GCの効率を向上させ、全体的なメモリ管理ロジックを簡素化できます。 -
MTypes構造体からのsysallocフィールドの削除:MTypes構造体は、MSpanに関連付けられた型情報を保持していました。以前は、sysallocというフィールドがあり、MTypes.dataが指すメモリがSysAllocによって割り当てられたものかどうかを追跡していました。settype_flushが常にmallocgcを使用するようになったため、このsysallocフィールドはもはや不要になりました。このフィールドの削除は、データ構造の簡素化と、不要な状態の追跡の排除を意味します。これにより、メモリフットプリントがわずかに減少し、コードの理解が容易になります。 -
runtime·settype_sysfree関数の削除: この関数は、MTypes.sysallocがtrueの場合に、SysAllocで割り当てられた型メタデータメモリをSysFreeを使って解放するために存在していました。SysAllocパスが完全に削除されたため、この関数はもはや呼び出されることがなくなり、デッドコードとなりました。したがって、このコミットで関数全体が削除されました。これは、コードベースのクリーンアップと、未使用の関数の削除による保守性の向上に貢献します。
これらの変更は、Goランタイムのメモリ管理がより統一され、効率的になったことを示しています。型メタデータの割り当てが通常のヒープ管理に統合されたことで、特殊なケースの処理が不要になり、ランタイムの複雑性が軽減されました。これは、Goのガベージコレクタとメモリ管理システムが継続的に改善されている証拠です。
関連リンク
- Go CL (Change List): https://golang.org/cl/12311045 このコミットに対応するGoのコードレビューシステム上の変更リストです。ここには、コミットの詳細な変更内容、レビューコメント、および関連する議論が含まれている可能性があります。
参考にした情報源リンク
- Goのソースコード(特に
src/pkg/runtime/ディレクトリ内のファイル) - Goのメモリ管理とガベージコレクションに関する公式ドキュメントやブログ記事(一般的な知識として)
- Goの
SysAllocおよびmallocgcの実装に関する情報(一般的な知識として) - Goの型情報とGCの連携に関する情報(一般的な知識として)
- https://github.com/golang/go/commit/0a904a3f2ed1c4c09acdb9116aa3843e5ad14dad (このコミットのGitHubページ)
- https://golang.org/cl/12311045 (このコミットのGo CLページ)
- Goのメモリ管理に関する一般的な解説記事や書籍(例: "Go言語による並行処理" など)# [インデックス 17020] ファイルの概要
このコミットは、Goランタイムのメモリ管理に関連するデッドコードの削除を目的としています。具体的には、型メタデータの割り当てにおいて SysAlloc を使用していた古い、もはや不要なコードパスが削除されました。
変更されたファイルは以下の通りです。
src/pkg/runtime/malloc.goc: メモリ割り当てと型情報フラッシュの主要なロジックが含まれるファイル。src/pkg/runtime/malloc.h: メモリ割り当てに関連するデータ構造と関数の宣言が含まれるヘッダーファイル。src/pkg/runtime/mgc0.c: ガベージコレクションの初期化と実行に関連するファイル。src/pkg/runtime/mheap.c: ヒープ管理に関連するファイル。
コミット
runtime: remove dead code
Remove dead code related to allocation of type metadata with SysAlloc.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/12311045
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/0a904a3f2ed1c4c09acdb9116aa3843e5ad14dad
元コミット内容
このコミットの元々の内容は、「runtime: remove dead code」(ランタイム: デッドコードの削除)であり、具体的には「Remove dead code related to allocation of type metadata with SysAlloc.」(SysAlloc を用いた型メタデータの割り当てに関連するデッドコードを削除)と説明されています。
変更の背景
このコミットの背景には、Goランタイムのメモリ管理戦略の進化があります。Goのガベージコレクタは、オブジェクトの型情報を利用してポインタを識別し、到達可能なオブジェクトを追跡します。この型メタデータは、ヒープ上に割り当てられたオブジェクトがどのような構造を持ち、どのフィールドがポインタであるかをGCに教えるために不可欠です。
以前のGoランタイムでは、この型メタデータを割り当てる際に、runtime·SysAlloc というシステムコールを直接利用するパスが存在していました。SysAlloc はOSから直接メモリを要求する低レベルな関数であり、Goランタイムのヒープ管理とは独立して動作します。
しかし、Goランタイムのメモリ管理が成熟し、特にガベージコレクタの改善が進むにつれて、型メタデータの割り当ても通常のヒープ割り当てメカニズム(runtime·mallocgc など)を通じて行われるようになりました。これにより、SysAlloc を直接使用するコードパスは不要となり、デッドコード(到達不能なコード、または実行されることがないコード)と化しました。
このコミットは、このような不要になった SysAlloc 関連のコードを削除することで、ランタイムのコードベースを整理し、保守性を向上させることを目的としています。デッドコードの削除は、コードの複雑性を減らし、将来の変更における潜在的なバグのリスクを低減する上で重要です。
前提知識の解説
このコミットを理解するためには、以下のGoランタイムの概念とメモリ管理の基礎知識が必要です。
-
Goランタイム (Go Runtime): Goプログラムの実行を管理する部分です。これには、ガベージコレクタ (GC)、スケジューラ (goroutineの管理)、メモリ割り当て、システムコールインターフェースなどが含まれます。Goプログラムは、OS上で直接実行されるのではなく、このランタイム上で動作します。
-
ガベージコレクション (Garbage Collection, GC): Goは自動メモリ管理を採用しており、開発者が手動でメモリを解放する必要はありません。GCは、プログラムがもはや参照しないメモリ領域(オブジェクト)を自動的に識別し、解放して再利用可能にするプロセスです。GoのGCは、並行マーク&スイープ方式を採用しており、プログラムの実行と並行して動作します。
-
型メタデータ (Type Metadata): GoのGCは、正確なGC(Precise GC)です。これは、GCがメモリ内のどの値がポインタであり、どの値がポインタでないかを正確に知っていることを意味します。この情報が「型メタデータ」です。各Goオブジェクトは、その型に関する情報(例えば、構造体のどのフィールドがポインタであるか、配列の要素がポインタであるかなど)をGCに提供します。これにより、GCはヒープを正確にスキャンし、到達可能なオブジェクトをマークできます。
-
SysAllocとSysFree:runtime·SysAllocは、GoランタイムがOSから直接メモリを要求するために使用する低レベルな関数です。これは、Goのヒープ管理システム(mheap)を介さずに、OSの仮想メモリ管理機能(例: Linuxのmmap、WindowsのVirtualAlloc)を直接利用します。runtime·SysFreeは、SysAllocで割り当てられたメモリをOSに返却するための対応する関数です。これらは、ランタイムの初期化や、非常に特殊なメモリ領域(例えば、スタックやGCメタデータの一部)の割り当てに使用されることがあります。 -
mallocgc:runtime·mallocgcは、Goランタイムの主要なメモリ割り当て関数です。これは、Goのヒープ(mheap)からメモリを割り当て、必要に応じてガベージコレクタをトリガーします。ほとんどのGoオブジェクトは、この関数を通じて割り当てられます。 -
MSpan: Goランタイムのメモリ管理において、MSpanは連続したページ(通常は8KB)のブロックを表すデータ構造です。ヒープはこれらのMSpanの集合として管理され、各MSpanは特定のサイズのオブジェクトを格納するために使用されます。MSpanには、そのスパンに割り当てられたオブジェクトの型情報(MTypes)も関連付けられます。 -
MTypes構造体:src/pkg/runtime/malloc.hで定義されているMTypes構造体は、MSpan内のオブジェクトの型情報を保持します。この構造体は、型情報の圧縮形式(compressionフィールド)と、実際の型データへのポインタ(dataフィールド)を含みます。このコミット以前は、sysallocというブール値のフィールドも持っており、dataフィールドがSysAllocによって割り当てられたものかどうかを示していました。 -
settype_flush関数:runtime·settype_flushは、MSpanに関連付けられた型情報を更新またはフラッシュする関数です。これは、MSpanが新しいオブジェクト型を格納するようになった場合や、型情報の表現形式が変更された場合(例えば、より効率的な圧縮形式に変換される場合)に呼び出されます。この関数内で、型情報を格納するためのメモリが割り当てられます。 -
settype_sysfree関数:runtime·settype_sysfreeは、MSpanに関連付けられた型情報がSysAllocによって割り当てられた場合に、そのメモリをSysFreeを使って解放するための関数でした。
技術的詳細
このコミットの技術的な詳細は、主に runtime·settype_flush 関数と MTypes 構造体からの sysalloc フィールドの削除に集約されます。
1. runtime·settype_flush 関数の変更:
-
引数の変更: 変更前:
runtime·settype_flush(M *mp, bool sysalloc)変更後:runtime·settype_flush(M *mp)sysallocというブール引数が削除されました。この引数は、型メタデータをSysAllocで割り当てるべきか、それとも通常のヒープ割り当て(mallocgc)で行うべきかを制御していました。この引数が不要になったのは、型メタデータの割り当てが常にmallocgcを通じて行われるようになったためです。 -
SysAlloc関連のコードパスの削除:runtime·settype_flushの内部では、MTypes_EmptyからMTypes_Bytesへ、またはMTypes_Wordsへ型情報が変換される際に、新しい型情報を格納するためのメモリを割り当てていました。 変更前は、sysalloc引数の値に基づいて、runtime·mallocgcを使用するか、runtime·SysAllocを使用するかの条件分岐がありました。このコミットでは、runtime·SysAllocを呼び出すパスが完全に削除され、常にruntime·mallocgcが使用されるようになりました。--- a/src/pkg/runtime/malloc.goc +++ b/src/pkg/runtime/malloc.goc @@ -563,14 +563,13 @@ runtime·persistentalloc(uintptr size, uintptr align) static Lock settype_lock; void -runtime·settype_flush(M *mp, bool sysalloc) +runtime·settype_flush(M *mp) { uintptr *buf, *endbuf; uintptr size, ofs, j, t; uintptr ntypes, nbytes2, nbytes3; uintptr *data2; byte *data3; - bool sysalloc3; void *v; uintptr typ, p; MSpan *s; @@ -605,20 +604,9 @@ runtime·settype_flush(M *mp, bool sysalloc) case MTypes_Empty: ntypes = (s->npages << PageShift) / size; nbytes3 = 8*sizeof(uintptr) + 1*ntypes; - - if(!sysalloc) { - data3 = runtime·mallocgc(nbytes3, 0, FlagNoProfiling|FlagNoPointers|FlagNoInvokeGC); - } else { - data3 = runtime·SysAlloc(nbytes3); - if(data3 == nil) - runtime·throw("runtime: cannot allocate memory"); - if(0) runtime·printf("settype(0->3): SysAlloc(%x) --> %p\n", (uint32)nbytes3, data3); - } - + data3 = runtime·mallocgc(nbytes3, 0, FlagNoProfiling|FlagNoPointers|FlagNoInvokeGC); s->types.compression = MTypes_Bytes; - s->types.sysalloc = sysalloc; s->types.data = (uintptr)data3; - ((uintptr*)data3)[1] = typ; data3[8*sizeof(uintptr) + ofs] = 1; break; @@ -643,20 +631,8 @@ runtime·settype_flush(M *mp, bool sysalloc) } else { ntypes = (s->npages << PageShift) / size; nbytes2 = ntypes * sizeof(uintptr); - - if(!sysalloc) { - data2 = runtime·mallocgc(nbytes2, 0, FlagNoProfiling|FlagNoPointers|FlagNoInvokeGC); - } else { - data2 = runtime·SysAlloc(nbytes2); - if(data2 == nil) - runtime·throw("runtime: cannot allocate memory"); - if(0) runtime·printf("settype.(3->2): SysAlloc(%x) --> %p\n", (uint32)nbytes2, data2); - } - - sysalloc3 = s->types.sysalloc; - + data2 = runtime·mallocgc(nbytes2, 0, FlagNoProfiling|FlagNoPointers|FlagNoInvokeGC); s->types.compression = MTypes_Words; - s->types.sysalloc = sysalloc; s->types.data = (uintptr)data2; // Move the contents of data3 to data2. Then deallocate data3.
2. MTypes 構造体からの sysalloc フィールドの削除:
-
src/pkg/runtime/malloc.h内のMTypes構造体からbool sysalloc;フィールドが削除されました。このフィールドは、MTypes.dataがSysAllocによって割り当てられたものかどうかを示すために使用されていましたが、SysAllocパスが削除されたため不要になりました。--- a/src/pkg/runtime/malloc.h +++ b/src/pkg/runtime/malloc.h @@ -334,7 +334,6 @@ enum struct MTypes { byte compression; // one of MTypes_* - bool sysalloc; // whether (void*)data is from runtime·SysAlloc uintptr data; };
3. runtime·settype_sysfree 関数の削除:
-
src/pkg/runtime/malloc.gocからruntime·settype_sysfree関数全体が削除されました。この関数は、MSpanの型情報がSysAllocで割り当てられた場合に、そのメモリを解放するために存在していましたが、SysAllocパスが削除されたため、この関数も不要になりました。--- a/src/pkg/runtime/malloc.goc +++ b/src/pkg/runtime/malloc.goc @@ -681,33 +651,6 @@ runtime·settype_flush(M *mp, bool sysalloc) mp->settype_bufsize = 0; } -void -runtime·settype_sysfree(MSpan *s) -{ - uintptr ntypes, nbytes; - - if(!s->types.sysalloc) - return; - - nbytes = (uintptr)-1; - - switch (s->types.compression) { - case MTypes_Words: - ntypes = (s->npages << PageShift) / s->elemsize; - nbytes = ntypes * sizeof(uintptr); - break; - case MTypes_Bytes: - ntypes = (s->npages << PageShift) / s->elemsize; - nbytes = 8*sizeof(uintptr) + 1*ntypes; - break; - } - - if(nbytes != (uintptr)-1) { - if(0) runtime·printf("settype: SysFree(%p,%x)\n", (void*)s->types.data, (uint32)nbytes); - runtime·SysFree((void*)s->types.data, nbytes); - } -} - uintptr runtime·gettype(void *v) {
4. 呼び出し箇所の変更:
src/pkg/runtime/mgc0.cとsrc/pkg/runtime/mheap.cでは、runtime·settype_flushの呼び出し箇所からfalse引数が削除されました。これは、settype_flushのシグネチャ変更に対応するものです。src/pkg/runtime/mheap.cでは、MSpanの解放時にs->types.sysallocをチェックし、runtime·settype_sysfree(s)を呼び出すコードが削除されました。これはsettype_sysfree関数自体が削除されたためです。
これらの変更は、Goランタイムが型メタデータの割り当てにおいて、SysAlloc を直接使用する特殊なケースを完全に排除し、一貫して mallocgc を使用するようになったことを示しています。これにより、メモリ管理ロジックが簡素化され、デッドコードが取り除かれました。
コアとなるコードの変更箇所
このコミットのコアとなるコードの変更箇所は、主に src/pkg/runtime/malloc.goc と src/pkg/runtime/malloc.h に集中しています。
src/pkg/runtime/malloc.goc:
-
runtime·settype_flush関数のシグネチャ変更 (行 563-564):--- a/src/pkg/runtime/malloc.goc +++ b/src/pkg/runtime/malloc.goc @@ -563,14 +563,13 @@ runtime·persistentalloc(uintptr size, uintptr align) static Lock settype_lock; void -runtime·settype_flush(M *mp, bool sysalloc) +runtime·settype_flush(M *mp) {bool sysalloc引数が削除されました。 -
runtime·settype_flush内のSysAlloc関連コードの削除 (行 605-615, 643-653):MTypes_EmptyからMTypes_Bytesへの変換時:--- a/src/pkg/runtime/malloc.goc +++ b/src/pkg/runtime/malloc.goc @@ -605,20 +604,9 @@ runtime·settype_flush(M *mp, bool sysalloc) case MTypes_Empty: ntypes = (s->npages << PageShift) / size; nbytes3 = 8*sizeof(uintptr) + 1*ntypes; - - if(!sysalloc) { - data3 = runtime·mallocgc(nbytes3, 0, FlagNoProfiling|FlagNoPointers|FlagNoInvokeGC); - } else { - data3 = runtime·SysAlloc(nbytes3); - if(data3 == nil) - runtime·throw("runtime: cannot allocate memory"); - if(0) runtime·printf("settype(0->3): SysAlloc(%x) --> %p\n", (uint32)nbytes3, data3); - } - + data3 = runtime·mallocgc(nbytes3, 0, FlagNoProfiling|FlagNoPointers|FlagNoInvokeGC); s->types.compression = MTypes_Bytes; - s->types.sysalloc = sysalloc; s->types.data = (uintptr)data3; - ((uintptr*)data3)[1] = typ; data3[8*sizeof(uintptr) + ofs] = 1; break;MTypes_BytesからMTypes_Wordsへの変換時:--- a/src/pkg/runtime/malloc.goc +++ b/src/pkg/runtime/malloc.goc @@ -643,20 +631,8 @@ runtime·settype_flush(M *mp, bool sysalloc) } else { ntypes = (s->npages << PageShift) / size; nbytes2 = ntypes * sizeof(uintptr); - - if(!sysalloc) { - data2 = runtime·mallocgc(nbytes2, 0, FlagNoProfiling|FlagNoPointers|FlagNoInvokeGC); - } else { - data2 = runtime·SysAlloc(nbytes2); - if(data2 == nil) - runtime·throw("runtime: cannot allocate memory"); - if(0) runtime·printf("settype.(3->2): SysAlloc(%x) --> %p\n", (uint32)nbytes2, data2); - } - - sysalloc3 = s->types.sysalloc; - + data2 = runtime·mallocgc(nbytes2, 0, FlagNoProfiling|FlagNoPointers|FlagNoInvokeGC); s->types.compression = MTypes_Words; - s->types.sysalloc = sysalloc; s->types.data = (uintptr)data2;SysAllocを使用する条件分岐と、s->types.sysallocの設定が削除され、常にruntime·mallocgcが使用されるようになりました。 -
runtime·settype_sysfree関数の削除 (行 681-713):--- a/src/pkg/runtime/malloc.goc +++ b/src/pkg/runtime/malloc.goc @@ -681,33 +651,6 @@ runtime·settype_flush(M *mp, bool sysalloc) mp->settype_bufsize = 0; } -void -runtime·settype_sysfree(MSpan *s) -{ - uintptr ntypes, nbytes; - - if(!s->types.sysalloc) - return; - - nbytes = (uintptr)-1; - - switch (s->types.compression) { - case MTypes_Words: - ntypes = (s->npages << PageShift) / s->elemsize; - nbytes = ntypes * sizeof(uintptr); - break; - case MTypes_Bytes: - ntypes = (s->npages << PageShift) / s->elemsize; - nbytes = 8*sizeof(uintptr) + 1*ntypes; - break; - } - - if(nbytes != (uintptr)-1) { - if(0) runtime·printf("settype: SysFree(%p,%x)\n", (void*)s->types.data, (uint32)nbytes); - runtime·SysFree((void*)s->types.data, nbytes); - } -} - uintptr runtime·gettype(void *v) {関数全体が削除されました。
src/pkg/runtime/malloc.h:
MTypes構造体からのsysallocフィールドの削除 (行 334-337):--- a/src/pkg/runtime/malloc.h +++ b/src/pkg/runtime/malloc.h @@ -334,7 +334,6 @@ enum struct MTypes { byte compression; // one of MTypes_* - bool sysalloc; // whether (void*)data is from runtime·SysAlloc uintptr data; };bool sysalloc;の行が削除されました。
これらの変更は、Goランタイムのメモリ管理における型メタデータの割り当てが、SysAlloc を介する特殊なケースから、一貫して通常のヒープ割り当てメカニズムに統合されたことを明確に示しています。
コアとなるコードの解説
このコミットのコアとなるコードの変更は、Goランタイムのメモリ管理における型メタデータの割り当て方法の根本的な簡素化と統一を意味します。
-
runtime·settype_flushからsysalloc引数とSysAllocパスの削除: 以前のruntime·settype_flush関数は、sysallocというブール引数を受け取っていました。この引数は、型メタデータを格納するためのメモリを、Goの通常のヒープ割り当てメカニズム(runtime·mallocgc)で取得するか、それともOSから直接メモリを要求する低レベルなruntime·SysAllocで取得するかを決定していました。 このコミットでは、sysalloc引数が削除され、関数内部のSysAllocを呼び出す条件分岐も削除されました。これにより、settype_flushは常にruntime·mallocgcを使用して型メタデータ用のメモリを割り当てるようになりました。 これは、Goランタイムのメモリ管理が十分に成熟し、型メタデータのような内部的なデータ構造の割り当ても、通常のヒープ管理の枠組みの中で効率的に行えるようになったことを示唆しています。SysAllocのような低レベルなシステムコールを避けることで、メモリの断片化を減らし、GCの効率を向上させ、全体的なメモリ管理ロジックを簡素化できます。 -
MTypes構造体からのsysallocフィールドの削除:MTypes構造体は、MSpanに関連付けられた型情報を保持していました。以前は、sysallocというフィールドがあり、MTypes.dataが指すメモリがSysAllocによって割り当てられたものかどうかを追跡していました。settype_flushが常にmallocgcを使用するようになったため、このsysallocフィールドはもはや不要になりました。このフィールドの削除は、データ構造の簡素化と、不要な状態の追跡の排除を意味します。これにより、メモリフットプリントがわずかに減少し、コードの理解が容易になります。 -
runtime·settype_sysfree関数の削除: この関数は、MTypes.sysallocがtrueの場合に、SysAllocで割り当てられた型メタデータメモリをSysFreeを使って解放するために存在していました。SysAllocパスが完全に削除されたため、この関数はもはや呼び出されることがなくなり、デッドコードとなりました。したがって、このコミットで関数全体が削除されました。これは、コードベースのクリーンアップと、未使用の関数の削除による保守性の向上に貢献します。
これらの変更は、Goランタイムのメモリ管理がより統一され、効率的になったことを示しています。型メタデータの割り当てが通常のヒープ管理に統合されたことで、特殊なケースの処理が不要になり、ランタイムの複雑性が軽減されました。これは、Goのガベージコレクタとメモリ管理システムが継続的に改善されている証拠です。
関連リンク
- Go CL (Change List): https://golang.org/cl/12311045 このコミットに対応するGoのコードレビューシステム上の変更リストです。ここには、コミットの詳細な変更内容、レビューコメント、および関連する議論が含まれている可能性があります。
参考にした情報源リンク
- Goのソースコード(特に
src/pkg/runtime/ディレクトリ内のファイル) - Goのメモリ管理とガベージコレクションに関する公式ドキュメントやブログ記事(一般的な知識として)
- Goの
SysAllocおよびmallocgcの実装に関する情報(一般的な知識として) - Goの型情報とGCの連携に関する情報(一般的な知識として)
- https://github.com/golang/go/commit/0a904a3f2ed1c4c09acdb9116aa3843e5ad14dad (このコミットのGitHubページ)
- https://golang.org/cl/12311045 (このコミットのGo CLページ)
- Goのメモリ管理に関する一般的な解説記事や書籍(例: "Go言語による並行処理" など)