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

[インデックス 17078] ファイルの概要

このコミットは、Go言語のランタイム、コンパイラ (cmd/gc)、およびCコンパイラ (cmd/cc) におけるガベージコレクション(GC)のスタックスキャンメカニズムを改善するものです。具体的には、ローカル変数領域のポインタをスキャンするために、従来の「保守的なルートスキャン」から、より「正確なビットマップベースのスキャン」へと移行しました。これにより、GCの精度と効率が向上します。

コミット

commit 73c93a404c16550ab1993aab1ce4a9b90c6f5772
Author: Carl Shapiro <cshapiro@google.com>
Date:   Wed Aug 7 12:47:01 2013 -0700

    cmd/cc, cmd/gc, runtime: emit bitmaps for scanning locals.
    
    Previously, all word aligned locations in the local variables
    area were scanned as conservative roots.  With this change, a
    bitmap is generated describing the locations of pointer values
    in local variables.
    
    With this change the argument bitmap information has been
    changed to only store information about arguments.  The locals
    member, has been removed.  In its place, the bitmap data for
    local variables is now used to store the size of locals.  If
    the size is negative, the magnitude indicates the size of the
    local variables area.
    
    R=rsc
    CC=golang-dev
    https://golang.org/cl/12328044

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/73c93a404c16550ab1993aab1ce4a9b90c6f5772

元コミット内容

このコミットの主な内容は以下の通りです。

  • ローカル変数スキャンの変更: 以前は、ローカル変数領域内のすべてのワードアラインされた位置が「保守的なルート」としてスキャンされていました。この変更により、ローカル変数内のポインタ値の位置を記述するビットマップが生成されるようになりました。
  • 引数ビットマップ情報の再定義: 引数に関するビットマップ情報は、引数のみの情報を格納するように変更されました。
  • locals メンバーの削除と置き換え: 以前の GCFunc 構造体にあった locals メンバーが削除され、その代わりにローカル変数用のビットマップデータがローカル変数のサイズを格納するために使用されるようになりました。もしサイズが負の値であれば、その絶対値がローカル変数領域のサイズを示します。

変更の背景

Goのガベージコレクションは、到達可能なオブジェクトを特定するために、プログラムのルート(グローバル変数、スタック上の変数、レジスタなど)からヒープを走査します。このコミット以前のGoのGCは、スタック上のローカル変数をスキャンする際に「保守的」なアプローチを採用していました。

保守的なGCスキャン (Conservative GC Scanning): 保守的なGCでは、メモリ上の値がポインタであるかどうかを確実に判断できない場合でも、それがポインタである可能性があると仮定してスキャンします。スタック上のワードアラインされたすべての値がポインタである可能性があると見なされ、それらが指す可能性のあるオブジェクトが到達可能としてマークされます。

このアプローチの利点は実装が単純であることですが、欠点もあります。

  1. メモリリークの可能性: ポインタではない値が偶然ポインタのように見える場合、その値が指すメモリ領域が誤って到達可能と判断され、解放されない可能性があります。これは「偽のポインタ」(false positive) と呼ばれ、メモリリークを引き起こす可能性があります。
  2. GC効率の低下: 不要なメモリ領域までスキャンするため、GCの実行時間が長くなる可能性があります。
  3. メモリ使用量の増加: 偽のポインタによって、本来解放されるべきメモリが保持され続けるため、メモリ使用量が増加する可能性があります。

このコミットは、ローカル変数スキャンを保守的から「正確」(precise) にすることで、これらの問題を解決しようとしました。正確なGCでは、コンパイラがポインタの位置を正確に識別するためのメタデータを生成し、GCはそのメタデータに基づいてポインタのみをスキャンします。これにより、偽のポインタの問題が解消され、GCの精度と効率が向上します。

前提知識の解説

ガベージコレクション (GC)

ガベージコレクションは、プログラムが動的に割り当てたメモリ領域のうち、もはやプログラムから到達できない(参照されていない)ものを自動的に解放するプロセスです。これにより、開発者は手動でのメモリ管理の負担から解放され、メモリリークやダングリングポインタといった一般的なメモリ関連のバグを防ぐことができます。

スタックスキャンとルート

GCは、プログラムが現在使用しているオブジェクトを特定するために、「ルート」と呼ばれる既知のポインタのセットから開始します。これらのルートには、グローバル変数、CPUレジスタ、そして実行中の関数のスタックフレーム内のローカル変数や引数が含まれます。GCはこれらのルートから到達可能なすべてのオブジェクトをマークし、マークされなかったオブジェクトは「ガベージ」として収集されます。

ポインタマップ (Pointer Map) / ビットマップ (Bitmap)

正確なGCを実現するために、コンパイラは各関数やデータ構造について、どのメモリ位置にポインタが含まれているかを記述するメタデータを生成します。このメタデータはしばしば「ポインタマップ」や「ビットマップ」として表現されます。ビットマップは、メモリ領域内の各ワードがポインタであるか否かを1ビットで示す配列のようなものです。例えば、1はポインタ、0は非ポインタを示します。

runtime·funcdata

Goランタイムには、各関数のメタデータ(関数名、ファイル名、行番号、GC情報など)を格納する funcdata テーブルがあります。runtime·funcdata は、このテーブルから特定の関数のGC関連のメタデータ(例えば、引数やローカル変数のポインタマップ)を取得するために使用されるランタイム関数です。

FUNCDATA_GCArgsFUNCDATA_GCLocals

これらは funcdata テーブル内でGC関連のメタデータを識別するための定数です。

  • FUNCDATA_GCArgs: 関数の引数領域におけるポインタのビットマップを指します。
  • FUNCDATA_GCLocals: 関数のローカル変数領域におけるポインタのビットマップを指します。

cmd/gccmd/cc

  • cmd/gc: Go言語の公式コンパイラです。Goのソースコードをコンパイルし、実行可能なバイナリを生成します。このプロセス中に、GCに必要なメタデータ(ポインタマップなど)も生成します。
  • cmd/cc: Goのツールチェーンに含まれるCコンパイラです。GoのランタイムはC言語で書かれた部分も含むため、cmd/cc が使用されます。このコミットでは、cmd/cc もGCメタデータの生成に関与するように変更されています。

技術的詳細

このコミットの核心は、コンパイラがスタック上のローカル変数と引数のポインタ位置を正確に記述するビットマップを生成し、ランタイムのGCがそのビットマップを利用してスキャンを行うように変更した点にあります。

コンパイラ (cmd/gc, cmd/cc) の役割: コンパイラは、関数のコンパイル時に、その関数のスタックフレームレイアウトを解析し、どのオフセットにポインタ値が格納されているかを特定します。この情報に基づいて、FUNCDATA_GCArgsFUNCDATA_GCLocals に対応するビットマップデータを生成し、最終的なバイナリの funcdata セクションに埋め込みます。

  • src/cmd/cc/pgen.c および src/cmd/gc/pgen.c: これらのファイルは、コンパイラのバックエンドの一部であり、コード生成とGCメタデータの生成を担当します。
    • FUNCDATA_GCArgsFUNCDATA_GCLocals という新しい FUNCDATA エントリが導入されました。
    • gclocalssym という新しいシンボルが導入され、ローカル変数のビットマップデータを格納します。
    • dumpgcargsdumpgclocals という関数が追加され、それぞれ引数とローカル変数のポインタビットマップを計算し、対応するシンボルにダンプします。
    • pointermap 関数は、引数のみのビットマップ生成に特化するように変更されました。
    • ローカル変数のサイズを負の値で表現する新しいメカニズムが導入されました。これは、FUNCDATA_GCLocals のビットマップデータが、ビットマップ自体ではなく、ローカル変数の総サイズを示す場合があることを意味します。これは、ローカル変数にポインタが含まれていない場合や、特定の最適化が適用される場合に、ビットマップの代わりにサイズ情報のみを格納することで、メタデータのオーバーヘッドを削減するためのものです。

ランタイム (src/pkg/runtime/mgc0.c) の役割: ランタイムのGCは、スタックフレームをスキャンする際に、runtime·funcdata を使用して、現在スキャンしている関数の FUNCDATA_GCArgsFUNCDATA_GCLocals に対応するビットマップデータを取得します。

  • src/pkg/runtime/mgc0.c: このファイルは、GoランタイムのGCの主要部分を実装しています。
    • GCFunc 構造体が BitVector 構造体に変更されました。BitVector は、ビットマップの長さ (n) と実際のビットデータ (data) を保持します。
    • scanbitvector という新しい関数が追加されました。この関数は、与えられた BitVector に基づいてメモリ領域をスキャンし、セットされているビットに対応するワードをGCルートとして addroot します。
    • addframeroots 関数が大幅に修正されました。
      • ローカル変数のスキャンロジックが変更され、FUNCDATA_GCLocals から取得した BitVector を使用するようになりました。
      • locals->n が負の場合(ローカル変数のサイズを示す場合)、その絶対値のサイズだけを保守的にスキャンします。
      • locals->n が正の場合(ビットマップが存在する場合)、scanbitvector を呼び出して正確にスキャンします。
      • 引数のスキャンロジックも変更され、FUNCDATA_GCArgs から取得した BitVector を使用して scanbitvector を呼び出すようになりました。

この変更により、GCはスタック上のポインタをより正確に識別できるようになり、偽のポインタによるメモリリークのリスクが低減し、GCのパフォーマンスが向上します。

コアとなるコードの変更箇所

src/cmd/cc/pgen.c

--- a/src/cmd/cc/pgen.c
+++ b/src/cmd/cc/pgen.c
@@ -78,9 +78,9 @@ void
 codgen(Node *n, Node *nn)
 {
 	Prog *sp;
-	Node *n1, nod, nod1;
-	Sym *gcsym;
-	static int ngcsym;
+	Node *n1, nod, nod1, nod2;
+	Sym *gcsym, *gclocalssym;
+	static int ngcsym, ngclocalssym;
 	static char namebuf[40];
 	int32 off;
 
@@ -116,7 +116,17 @@ codgen(Node *n, Node *nn)
 	nod.op = ONAME;
 	nod.sym = gcsym;
 	nod.class = CSTATIC;
-	gins(AFUNCDATA, nodconst(FUNCDATA_GC), &nod);
+	gins(AFUNCDATA, nodconst(FUNCDATA_GCArgs), &nod);
+
+	snprint(namebuf, sizeof(namebuf), "gclocalssym·%d", ngclocalssym++);
+	gclocalssym = slookup(namebuf);
+	gclocalssym->class = CSTATIC;
+
+	memset(&nod2, 0, sizeof(nod2));
+	nod2.op = ONAME;
+	nod2.sym = gclocalssym;
+	nod2.class = CSTATIC;
+	gins(AFUNCDATA, nodconst(FUNCDATA_GCLocals), &nod2);
 
 	/*
 	 * isolate first argument
@@ -165,11 +175,15 @@ codgen(Node *n, Node *nn)
 	// That said, we've been using stkoff for months
 	// and nothing too terrible has happened.
 	off = 0;
-	gextern(gcsym, nodconst(stkoff), off, 4); // locals
-	off += 4;
 	off = pointermap(gcsym, off); // nptrs and ptrs[...]
 	gcsym->type = typ(0, T);
 	gcsym->type->width = off;
+
+	off = 0;
+	gextern(gclocalssym, nodconst(-stkoff), off, 4); // locals
+	off += 4;
+	gclocalssym->type = typ(0, T);
+	gclocalssym->type->width = off;
 }
 
 void

src/cmd/gc/pgen.c

--- a/src/cmd/gc/pgen.c
+++ b/src/cmd/gc/pgen.c
@@ -9,22 +9,22 @@
 #include	"../../pkg/runtime/funcdata.h"
 
 static void allocauto(Prog* p);
-static int pointermap(Sym*, int, Node*);
-static void gcsymbol(Sym*, Node*);
+static void dumpgcargs(Node*, Sym*);
+static void dumpgclocals(Node*, Sym*);
 
 void
 compile(Node *fn)
 {
 	Plist *pl;
-	Node nod1, *n, *gcnod;
+	Node nod1, *n, *gcargsnod, *gclocalsnod;
 	Prog *ptxt, *p, *p1;
 	int32 lno;
 	Type *t;
 	Iter save;
 	vlong oldstksize;
 	NodeList *l;
-	Sym *gcsym;
-	static int ngcsym;
+	Sym *gcargssym, *gclocalssym;
+	static int ngcargs, ngclocals;
 
 	if(newproc == N) {
 		newproc = sysfunc("newproc");
@@ -93,13 +93,21 @@ compile(Node *fn)
 
 	ginit();
 
-	snprint(namebuf, sizeof namebuf, "gc·%d", ngcsym++);
-	gcsym = lookup(namebuf);
-	gcnod = newname(gcsym);
-	gcnod->class = PEXTERN;
+	snprint(namebuf, sizeof namebuf, "gcargs·%d", ngcargs++);
+	gcargssym = lookup(namebuf);
+	gcargsnod = newname(gcargssym);
+	gcargsnod->class = PEXTERN;
 
-	nodconst(&nod1, types[TINT32], FUNCDATA_GC);
-	gins(AFUNCDATA, &nod1, gcnod);
+	nodconst(&nod1, types[TINT32], FUNCDATA_GCArgs);
+	gins(AFUNCDATA, &nod1, gcargsnod);
+
+	snprint(namebuf, sizeof(namebuf), "gclocals·%d", ngclocals++);
+	gclocalssym = lookup(namebuf);
+	gclocalsnod = newname(gclocalssym);
+	gclocalsnod->class = PEXTERN;
+
+	nodconst(&nod1, types[TINT32], FUNCDATA_GCLocals);
+	gins(AFUNCDATA, &nod1, gclocalsnod);
 
 	for(t=curfn->paramfld; t; t=t->down)
 		gtrack(tracksym(t->type));
@@ -159,37 +167,29 @@ compile(Node *fn)
 
 	oldstksize = stksize;
 	allocauto(ptxt);
-	
-	// Emit garbage collection symbol.
-	gcsymbol(gcsym, fn);
 
 	if(0)
 		print("allocauto: %lld to %lld\n", oldstksize, (vlong)stksize);
 
 	setlineno(curfn);
-	if((int64)stksize+maxarg > (1ULL<<31))
+	if((int64)stksize+maxarg > (1ULL<<31)) {
 		yyerror("stack frame too large (>2GB)");
+		goto ret;
+	}
 
 	defframe(ptxt);
 
 	if(0)
 		frame(0);
 
+	// Emit garbage collection symbols.
+	dumpgcargs(fn, gcargssym);
+	dumpgclocals(curfn, gclocalssym);
+
 ret:
 	lineno = lno;
 }
 
-- static void
-- gcsymbol(Sym *gcsym, Node *fn)
-- {
-- 	int off;
--
-- 	off = 0;
-- 	off = duint32(gcsym, off, stksize); // size of local block
-- 	off = pointermap(gcsym, off, fn); // pointer bitmap for args (must be last)
-- 	ggloblsym(gcsym, off, 0, 1);
-- }
--
 static void
 walktype1(Type *t, vlong *xoffset, Bvec *bv)
 {
@@ -296,13 +296,15 @@ walktype(Type *type, Bvec *bv)
 }
 
 // Compute a bit vector to describes the pointer containing locations
-// in the argument list.
-static int
-pointermap(Sym *gcsym, int off, Node *fn)
+// in the in and out argument list and dump the bitvector length and
+// data to the provided symbol.
+static void
+dumpgcargs(Node *fn, Sym *sym)
 {
 	Type *thistype, *inargtype, *outargtype;
 	Bvec *bv;
 	int32 i;
+	int off;
 
 	thistype = getthisx(fn->type);
 	inargtype = getinargx(fn->type);
@@ -314,11 +316,42 @@ pointermap(Sym *gcsym, int off, Node *fn)
 	if(outargtype != nil)
 		walktype(outargtype, bv);
-	off = duint32(gcsym, off, bv->n);
+	off = duint32(sym, 0, bv->n);
 	for(i = 0; i < bv->n; i += 32)
-		off = duint32(gcsym, off, bv->b[i/32]);
+		off = duint32(sym, off, bv->b[i/32]);
+	free(bv);
+	ggloblsym(sym, off, 0, 1);
+}
+
+// Compute a bit vector to describes the pointer containing locations
+// in local variables and dumps the bitvector length and data out to
+// the provided symbol.
+static void
+dumpgclocals(Node* fn, Sym *sym)
+{
+	Bvec *bv;
+	NodeList *ll;
+	Node *node;
+	vlong xoffset;
+	int32 i;
+	int off;
+
+	bv = bvalloc(rnd(stksize, widthptr) / widthptr);
+	for(ll = fn->dcl; ll != nil; ll = ll->next) {
+		node = ll->n;
+		if(node->class == PAUTO && node->op == ONAME) {
+			if(haspointers(node->type)) {
+				xoffset = node->xoffset + rnd(stksize,widthptr);
+				walktype1(node->type, &xoffset, bv);
+			}
+		}
+	}
+	off = duint32(sym, 0, bv->n);
+	for(i = 0; i < bv->n; i += 32) {
+		off = duint32(sym, off, bv->b[i/32]);
+	}
 	free(bv);
-	return off;
+	ggloblsym(sym, off, 0, 1);
 }
 
 // Sort the list of stack variables.  autos after anything else,

src/pkg/runtime/funcdata.h

--- a/src/pkg/runtime/funcdata.h
+++ b/src/pkg/runtime/funcdata.h
@@ -9,7 +9,8 @@
 
 #define PCDATA_ArgSize 0 /* argument size at CALL instruction */
 
-#define FUNCDATA_GC 0 /* garbage collector block */
+#define FUNCDATA_GCArgs 0 /* garbage collector blocks */
+#define FUNCDATA_GCLocals 1
 
 // To be used in assembly.
 #define ARGSIZE(n) PCDATA $PCDATA_ArgSize, $n

src/pkg/runtime/mgc0.c

--- a/src/pkg/runtime/mgc0.c
+++ b/src/pkg/runtime/mgc0.c
@@ -1387,56 +1387,74 @@ addroot(Obj obj)
 
 extern byte pclntab[]; // base for f->ptrsoff
 
-typedef struct GCFunc GCFunc;\n-struct GCFunc
+typedef struct BitVector BitVector;\n+struct BitVector
 {
-\tuint32\tlocals; // size of local variables in bytes
-\tuint32\tnptrs; // number of words that follow
-\tuint32\tptrs[1]; // bitmap of pointers in arguments
+\tint32 n;\n+\tuint32 data[];
 };
 
+// Starting from scanp, scans words corresponding to set bits.
+static void
+scanbitvector(byte *scanp, BitVector *bv)
+{
+	uint32 *wp;
+	uint32 w;
+	int32 i, remptrs;
+
+	wp = bv->data;
+	for(remptrs = bv->n; remptrs > 0; remptrs -= 32) {
+		w = *wp++;
+		if(remptrs < 32)
+			i = remptrs;
+		else
+			i = 32;
+		for(; i > 0; i--) {
+			if(w & 1)
+				addroot((Obj){scanp, PtrSize, 0});
+			w >>= 1;
+			scanp += PtrSize;
+		}
+	}
+}
+
 // Scan a stack frame: local variables and function arguments/results.
 static void
 addframeroots(Stkframe *frame, void*)
 {
 	Func *f;
-\tbyte *ap;\n-\tint32 i, j, nuintptr;\n-\tuint32 w, b;\n-\tGCFunc *gcf;
+\tBitVector *args, *locals;\n+\tuintptr size;
 
 	f = frame->fn;
-\tgcf = runtime·funcdata(f, FUNCDATA_GC);
-\t\n
+\n
 	// Scan local variables if stack frame has been allocated.
-\ti = frame->varp - (byte*)frame->sp;
-\tif(i > 0) {
-\t\tif(gcf == nil)
-\t\t\taddroot((Obj){frame->varp - i, i, 0});
-\t\telse if(i >= gcf->locals)
-\t\t\taddroot((Obj){frame->varp - gcf->locals, gcf->locals, 0});
+\t// Use pointer information if known.
+\tif(frame->varp > (byte*)frame->sp) {
+\t\tlocals = runtime·funcdata(f, FUNCDATA_GCLocals);
+\t\tif(locals == nil) {
+\t\t\t// No locals information, scan everything.
+\t\t\tsize = frame->varp - (byte*)frame->sp;
+\t\t\taddroot((Obj){frame->varp - size, size, 0});
+\t\t} else if(locals->n < 0) {
+\t\t\t// Locals size information, scan just the
+\t\t\t// locals.
+\t\t\tsize = -locals->n;
+\t\t\taddroot((Obj){frame->varp - size, size, 0});
+\t\t} else if(locals->n > 0) {
+\t\t\t// Locals bitmap information, scan just the
+\t\t\t// pointers in locals.
+\t\t\tsize = locals->n*PtrSize;
+\t\t\tscanbitvector(frame->varp - size, locals);
+\t\t}
 	}
 
 	// Scan arguments.
 	// Use pointer information if known.
-\tif(f->args > 0 && gcf != nil && gcf->nptrs > 0) {
-\t\tap = frame->argp;
-\t\tnuintptr = f->args / sizeof(uintptr);
-\t\tfor(i = 0; i < gcf->nptrs; i++) {
-\t\t\tw = gcf->ptrs[i];
-\t\t\tb = 1;
-\t\t\tj = nuintptr;
-\t\t\tif(j > 32)\n-\t\t\t\tj = 32;
-\t\t\tfor(; j > 0; j--) {
-\t\t\t\tif(w & b)\n-\t\t\t\t\taddroot((Obj){ap, sizeof(uintptr), 0});
-\t\t\t\tb <<= 1;\n-\t\t\t\tap += sizeof(uintptr);
-\t\t\t}
-\t\t\tnuintptr -= 32;
-\t\t}
-\t} else
+\targs = runtime·funcdata(f, FUNCDATA_GCArgs);\n+\tif(args != nil && args->n > 0)\n+\t\tscanbitvector(frame->argp, args);\n+\telse
 	\taddroot((Obj){frame->argp, frame->arglen, 0});
 }
 

コアとなるコードの解説

src/cmd/cc/pgen.c および src/cmd/gc/pgen.c の変更点

これらのファイルはGoコンパイラのコード生成部分であり、GCがスタックをスキャンするために必要なメタデータを生成する役割を担っています。

  • FUNCDATA_GCArgsFUNCDATA_GCLocals の導入: 以前は FUNCDATA_GC という単一の定数でGC関連のデータブロックを指していましたが、この変更により、引数とローカル変数でそれぞれ異なるビットマップを指す FUNCDATA_GCArgsFUNCDATA_GCLocals が導入されました。これにより、GCメタデータの粒度が向上し、より正確なスキャンが可能になります。

  • gclocalssym の導入: ローカル変数のポインタビットマップを格納するための新しいシンボル gclocalssym が導入されました。これは、各関数に対して一意に生成されます。

  • dumpgcargsdumpgclocals 関数の追加: dumpgcargs は関数の引数領域のポインタビットマップを計算し、dumpgclocals はローカル変数領域のポインタビットマップを計算します。これらの関数は、スタックフレーム内の各変数について、それがポインタであるかどうかを判断し、ビットマップを構築します。 特に dumpgclocals では、ローカル変数にポインタが含まれている場合にのみ walktype1 を呼び出してビットマップを生成します。また、gextern(gclocalssym, nodconst(-stkoff), off, 4); のように、stkoff (スタックフレームのサイズ) を負の値で格納するロジックが追加されています。これは、ローカル変数にポインタがない場合など、ビットマップを生成する必要がない場合に、ローカル変数の総サイズをGCに伝えるための最適化です。

  • gcsymbol 関数の削除: 以前の gcsymbol 関数は、引数とローカル変数の両方のGC情報を単一のシンボルにまとめていましたが、新しいアプローチでは引数とローカル変数が分離されたため、この関数は不要となり削除されました。

src/pkg/runtime/funcdata.h の変更点

  • FUNCDATA_GCArgsFUNCDATA_GCLocals の定義: FUNCDATA_GCFUNCDATA_GCArgsFUNCDATA_GCLocals に分割され、それぞれ異なる整数値が割り当てられました。これにより、ランタイムは funcdata テーブルから引数とローカル変数のGC情報を個別に取得できるようになります。

src/pkg/runtime/mgc0.c の変更点

このファイルはGoランタイムのGCのコアロジックを含んでいます。

  • GCFunc から BitVector への変更: 以前の GCFunc 構造体は、ローカル変数のサイズ (locals) と引数のポインタ数 (nptrs)、そして引数のビットマップ (ptrs) を持っていました。これが BitVector 構造体に変更され、ビットマップの長さ (n) と実際のビットデータ (data) を持つようになりました。この変更は、GCメタデータの表現をより汎用的なビットマップ形式に統一したことを示しています。

  • scanbitvector 関数の追加: この関数は、BitVector を受け取り、そのビットマップに基づいてメモリ領域をスキャンします。ビットマップの各ビットが 1 であれば、対応するメモリ位置がポインタであると判断し、そのポインタをGCルートとして addroot 関数に渡します。これにより、GCはポインタのみを正確にスキャンできるようになります。

  • addframeroots 関数の修正: addframeroots は、スタックフレーム(ローカル変数と引数)をスキャンしてGCルートを特定する主要な関数です。

    • ローカル変数のスキャン: runtime·funcdata(f, FUNCDATA_GCLocals) を呼び出して、ローカル変数の BitVector を取得します。
      • もし localsnil であれば(ローつまり、ローカル変数に関するGC情報がない場合)、以前と同様にローカル変数領域全体を保守的にスキャンします。
      • もし locals->n が負の値であれば、それはローカル変数領域の総サイズを示しているため、そのサイズだけを保守的にスキャンします。これは、ローカル変数にポインタが含まれていない場合の最適化です。
      • もし locals->n が正の値であれば、それはビットマップの長さを示しているため、scanbitvector を呼び出して、ローカル変数領域内のポインタを正確にスキャンします。
    • 引数のスキャン: runtime·funcdata(f, FUNCDATA_GCArgs) を呼び出して、引数の BitVector を取得します。
      • もし argsnil でない、かつ args->n が正の値であれば、scanbitvector を呼び出して引数領域内のポインタを正確にスキャンします。
      • それ以外の場合は、引数領域全体を保守的にスキャンします。

これらの変更により、GoのGCはスタック上のポインタをより効率的かつ正確に識別できるようになり、GCのパフォーマンスと信頼性が向上しました。

関連リンク

  • Go言語のガベージコレクションに関する公式ドキュメント (当時の情報を含む可能性のあるもの):

参考にした情報源リンク

これらの情報は、コミットが行われた2013年当時のGoのGCの状況と、その後の進化を理解する上で役立ちます。