Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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ランタイムの概念とメモリ管理の基礎知識が必要です。

  1. Goランタイム (Go Runtime): Goプログラムの実行を管理する部分です。これには、ガベージコレクタ (GC)、スケジューラ (goroutineの管理)、メモリ割り当て、システムコールインターフェースなどが含まれます。Goプログラムは、OS上で直接実行されるのではなく、このランタイム上で動作します。

  2. ガベージコレクション (Garbage Collection, GC): Goは自動メモリ管理を採用しており、開発者が手動でメモリを解放する必要はありません。GCは、プログラムがもはや参照しないメモリ領域(オブジェクト)を自動的に識別し、解放して再利用可能にするプロセスです。GoのGCは、並行マーク&スイープ方式を採用しており、プログラムの実行と並行して動作します。

  3. 型メタデータ (Type Metadata): GoのGCは、正確なGC(Precise GC)です。これは、GCがメモリ内のどの値がポインタであり、どの値がポインタでないかを正確に知っていることを意味します。この情報が「型メタデータ」です。各Goオブジェクトは、その型に関する情報(例えば、構造体のどのフィールドがポインタであるか、配列の要素がポインタであるかなど)をGCに提供します。これにより、GCはヒープを正確にスキャンし、到達可能なオブジェクトをマークできます。

  4. SysAllocSysFree: runtime·SysAlloc は、GoランタイムがOSから直接メモリを要求するために使用する低レベルな関数です。これは、Goのヒープ管理システム(mheap)を介さずに、OSの仮想メモリ管理機能(例: Linuxのmmap、WindowsのVirtualAlloc)を直接利用します。runtime·SysFree は、SysAlloc で割り当てられたメモリをOSに返却するための対応する関数です。これらは、ランタイムの初期化や、非常に特殊なメモリ領域(例えば、スタックやGCメタデータの一部)の割り当てに使用されることがあります。

  5. mallocgc: runtime·mallocgc は、Goランタイムの主要なメモリ割り当て関数です。これは、Goのヒープ(mheap)からメモリを割り当て、必要に応じてガベージコレクタをトリガーします。ほとんどのGoオブジェクトは、この関数を通じて割り当てられます。

  6. MSpan: Goランタイムのメモリ管理において、MSpan は連続したページ(通常は8KB)のブロックを表すデータ構造です。ヒープはこれらのMSpanの集合として管理され、各MSpanは特定のサイズのオブジェクトを格納するために使用されます。MSpanには、そのスパンに割り当てられたオブジェクトの型情報(MTypes)も関連付けられます。

  7. MTypes 構造体: src/pkg/runtime/malloc.h で定義されている MTypes 構造体は、MSpan 内のオブジェクトの型情報を保持します。この構造体は、型情報の圧縮形式(compressionフィールド)と、実際の型データへのポインタ(dataフィールド)を含みます。このコミット以前は、sysallocというブール値のフィールドも持っており、dataフィールドがSysAllocによって割り当てられたものかどうかを示していました。

  8. settype_flush 関数: runtime·settype_flush は、MSpan に関連付けられた型情報を更新またはフラッシュする関数です。これは、MSpan が新しいオブジェクト型を格納するようになった場合や、型情報の表現形式が変更された場合(例えば、より効率的な圧縮形式に変換される場合)に呼び出されます。この関数内で、型情報を格納するためのメモリが割り当てられます。

  9. 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.dataSysAlloc によって割り当てられたメモリを指しているかどうかを示すために使用されていましたが、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.csrc/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.gocsrc/pkg/runtime/malloc.h に集中しています。

src/pkg/runtime/malloc.goc:

  1. 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 引数が削除されました。

  2. 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 が使用されるようになりました。

  3. 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:

  1. 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ランタイムのメモリ管理における型メタデータの割り当て方法の根本的な簡素化と統一を意味します。

  1. runtime·settype_flush から sysalloc 引数と SysAlloc パスの削除: 以前の runtime·settype_flush 関数は、sysalloc というブール引数を受け取っていました。この引数は、型メタデータを格納するためのメモリを、Goの通常のヒープ割り当てメカニズム(runtime·mallocgc)で取得するか、それともOSから直接メモリを要求する低レベルな runtime·SysAlloc で取得するかを決定していました。 このコミットでは、sysalloc 引数が削除され、関数内部の SysAlloc を呼び出す条件分岐も削除されました。これにより、settype_flush は常に runtime·mallocgc を使用して型メタデータ用のメモリを割り当てるようになりました。 これは、Goランタイムのメモリ管理が十分に成熟し、型メタデータのような内部的なデータ構造の割り当ても、通常のヒープ管理の枠組みの中で効率的に行えるようになったことを示唆しています。SysAlloc のような低レベルなシステムコールを避けることで、メモリの断片化を減らし、GCの効率を向上させ、全体的なメモリ管理ロジックを簡素化できます。

  2. MTypes 構造体からの sysalloc フィールドの削除: MTypes 構造体は、MSpan に関連付けられた型情報を保持していました。以前は、sysalloc というフィールドがあり、MTypes.data が指すメモリが SysAlloc によって割り当てられたものかどうかを追跡していました。 settype_flush が常に mallocgc を使用するようになったため、この sysalloc フィールドはもはや不要になりました。このフィールドの削除は、データ構造の簡素化と、不要な状態の追跡の排除を意味します。これにより、メモリフットプリントがわずかに減少し、コードの理解が容易になります。

  3. runtime·settype_sysfree 関数の削除: この関数は、MTypes.sysalloctrue の場合に、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ランタイムの概念とメモリ管理の基礎知識が必要です。

  1. Goランタイム (Go Runtime): Goプログラムの実行を管理する部分です。これには、ガベージコレクタ (GC)、スケジューラ (goroutineの管理)、メモリ割り当て、システムコールインターフェースなどが含まれます。Goプログラムは、OS上で直接実行されるのではなく、このランタイム上で動作します。

  2. ガベージコレクション (Garbage Collection, GC): Goは自動メモリ管理を採用しており、開発者が手動でメモリを解放する必要はありません。GCは、プログラムがもはや参照しないメモリ領域(オブジェクト)を自動的に識別し、解放して再利用可能にするプロセスです。GoのGCは、並行マーク&スイープ方式を採用しており、プログラムの実行と並行して動作します。

  3. 型メタデータ (Type Metadata): GoのGCは、正確なGC(Precise GC)です。これは、GCがメモリ内のどの値がポインタであり、どの値がポインタでないかを正確に知っていることを意味します。この情報が「型メタデータ」です。各Goオブジェクトは、その型に関する情報(例えば、構造体のどのフィールドがポインタであるか、配列の要素がポインタであるかなど)をGCに提供します。これにより、GCはヒープを正確にスキャンし、到達可能なオブジェクトをマークできます。

  4. SysAllocSysFree: runtime·SysAlloc は、GoランタイムがOSから直接メモリを要求するために使用する低レベルな関数です。これは、Goのヒープ管理システム(mheap)を介さずに、OSの仮想メモリ管理機能(例: Linuxのmmap、WindowsのVirtualAlloc)を直接利用します。runtime·SysFree は、SysAlloc で割り当てられたメモリをOSに返却するための対応する関数です。これらは、ランタイムの初期化や、非常に特殊なメモリ領域(例えば、スタックやGCメタデータの一部)の割り当てに使用されることがあります。

  5. mallocgc: runtime·mallocgc は、Goランタイムの主要なメモリ割り当て関数です。これは、Goのヒープ(mheap)からメモリを割り当て、必要に応じてガベージコレクタをトリガーします。ほとんどのGoオブジェクトは、この関数を通じて割り当てられます。

  6. MSpan: Goランタイムのメモリ管理において、MSpan は連続したページ(通常は8KB)のブロックを表すデータ構造です。ヒープはこれらのMSpanの集合として管理され、各MSpanは特定のサイズのオブジェクトを格納するために使用されます。MSpanには、そのスパンに割り当てられたオブジェクトの型情報(MTypes)も関連付けられます。

  7. MTypes 構造体: src/pkg/runtime/malloc.h で定義されている MTypes 構造体は、MSpan 内のオブジェクトの型情報を保持します。この構造体は、型情報の圧縮形式(compressionフィールド)と、実際の型データへのポインタ(dataフィールド)を含みます。このコミット以前は、sysallocというブール値のフィールドも持っており、dataフィールドがSysAllocによって割り当てられたものかどうかを示していました。

  8. settype_flush 関数: runtime·settype_flush は、MSpan に関連付けられた型情報を更新またはフラッシュする関数です。これは、MSpan が新しいオブジェクト型を格納するようになった場合や、型情報の表現形式が変更された場合(例えば、より効率的な圧縮形式に変換される場合)に呼び出されます。この関数内で、型情報を格納するためのメモリが割り当てられます。

  9. 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.dataSysAlloc によって割り当てられたものかどうかを示すために使用されていましたが、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.csrc/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.gocsrc/pkg/runtime/malloc.h に集中しています。

src/pkg/runtime/malloc.goc:

  1. 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 引数が削除されました。

  2. 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 が使用されるようになりました。

  3. 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:

  1. 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ランタイムのメモリ管理における型メタデータの割り当て方法の根本的な簡素化と統一を意味します。

  1. runtime·settype_flush から sysalloc 引数と SysAlloc パスの削除: 以前の runtime·settype_flush 関数は、sysalloc というブール引数を受け取っていました。この引数は、型メタデータを格納するためのメモリを、Goの通常のヒープ割り当てメカニズム(runtime·mallocgc)で取得するか、それともOSから直接メモリを要求する低レベルな runtime·SysAlloc で取得するかを決定していました。 このコミットでは、sysalloc 引数が削除され、関数内部の SysAlloc を呼び出す条件分岐も削除されました。これにより、settype_flush は常に runtime·mallocgc を使用して型メタデータ用のメモリを割り当てるようになりました。 これは、Goランタイムのメモリ管理が十分に成熟し、型メタデータのような内部的なデータ構造の割り当ても、通常のヒープ管理の枠組みの中で効率的に行えるようになったことを示唆しています。SysAlloc のような低レベルなシステムコールを避けることで、メモリの断片化を減らし、GCの効率を向上させ、全体的なメモリ管理ロジックを簡素化できます。

  2. MTypes 構造体からの sysalloc フィールドの削除: MTypes 構造体は、MSpan に関連付けられた型情報を保持していました。以前は、sysalloc というフィールドがあり、MTypes.data が指すメモリが SysAlloc によって割り当てられたものかどうかを追跡していました。 settype_flush が常に mallocgc を使用するようになったため、この sysalloc フィールドはもはや不要になりました。このフィールドの削除は、データ構造の簡素化と、不要な状態の追跡の排除を意味します。これにより、メモリフットプリントがわずかに減少し、コードの理解が容易になります。

  3. runtime·settype_sysfree 関数の削除: この関数は、MTypes.sysalloctrue の場合に、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言語による並行処理" など)