[インデックス 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言語による並行処理" など)