[インデックス 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では、メモリ上の値がポインタであるかどうかを確実に判断できない場合でも、それがポインタである可能性があると仮定してスキャンします。スタック上のワードアラインされたすべての値がポインタである可能性があると見なされ、それらが指す可能性のあるオブジェクトが到達可能としてマークされます。
このアプローチの利点は実装が単純であることですが、欠点もあります。
- メモリリークの可能性: ポインタではない値が偶然ポインタのように見える場合、その値が指すメモリ領域が誤って到達可能と判断され、解放されない可能性があります。これは「偽のポインタ」(false positive) と呼ばれ、メモリリークを引き起こす可能性があります。
- GC効率の低下: 不要なメモリ領域までスキャンするため、GCの実行時間が長くなる可能性があります。
- メモリ使用量の増加: 偽のポインタによって、本来解放されるべきメモリが保持され続けるため、メモリ使用量が増加する可能性があります。
このコミットは、ローカル変数スキャンを保守的から「正確」(precise) にすることで、これらの問題を解決しようとしました。正確なGCでは、コンパイラがポインタの位置を正確に識別するためのメタデータを生成し、GCはそのメタデータに基づいてポインタのみをスキャンします。これにより、偽のポインタの問題が解消され、GCの精度と効率が向上します。
前提知識の解説
ガベージコレクション (GC)
ガベージコレクションは、プログラムが動的に割り当てたメモリ領域のうち、もはやプログラムから到達できない(参照されていない)ものを自動的に解放するプロセスです。これにより、開発者は手動でのメモリ管理の負担から解放され、メモリリークやダングリングポインタといった一般的なメモリ関連のバグを防ぐことができます。
スタックスキャンとルート
GCは、プログラムが現在使用しているオブジェクトを特定するために、「ルート」と呼ばれる既知のポインタのセットから開始します。これらのルートには、グローバル変数、CPUレジスタ、そして実行中の関数のスタックフレーム内のローカル変数や引数が含まれます。GCはこれらのルートから到達可能なすべてのオブジェクトをマークし、マークされなかったオブジェクトは「ガベージ」として収集されます。
ポインタマップ (Pointer Map) / ビットマップ (Bitmap)
正確なGCを実現するために、コンパイラは各関数やデータ構造について、どのメモリ位置にポインタが含まれているかを記述するメタデータを生成します。このメタデータはしばしば「ポインタマップ」や「ビットマップ」として表現されます。ビットマップは、メモリ領域内の各ワードがポインタであるか否かを1ビットで示す配列のようなものです。例えば、1
はポインタ、0
は非ポインタを示します。
runtime·funcdata
Goランタイムには、各関数のメタデータ(関数名、ファイル名、行番号、GC情報など)を格納する funcdata
テーブルがあります。runtime·funcdata
は、このテーブルから特定の関数のGC関連のメタデータ(例えば、引数やローカル変数のポインタマップ)を取得するために使用されるランタイム関数です。
FUNCDATA_GCArgs
と FUNCDATA_GCLocals
これらは funcdata
テーブル内でGC関連のメタデータを識別するための定数です。
FUNCDATA_GCArgs
: 関数の引数領域におけるポインタのビットマップを指します。FUNCDATA_GCLocals
: 関数のローカル変数領域におけるポインタのビットマップを指します。
cmd/gc
と cmd/cc
cmd/gc
: Go言語の公式コンパイラです。Goのソースコードをコンパイルし、実行可能なバイナリを生成します。このプロセス中に、GCに必要なメタデータ(ポインタマップなど)も生成します。cmd/cc
: Goのツールチェーンに含まれるCコンパイラです。GoのランタイムはC言語で書かれた部分も含むため、cmd/cc
が使用されます。このコミットでは、cmd/cc
もGCメタデータの生成に関与するように変更されています。
技術的詳細
このコミットの核心は、コンパイラがスタック上のローカル変数と引数のポインタ位置を正確に記述するビットマップを生成し、ランタイムのGCがそのビットマップを利用してスキャンを行うように変更した点にあります。
コンパイラ (cmd/gc
, cmd/cc
) の役割:
コンパイラは、関数のコンパイル時に、その関数のスタックフレームレイアウトを解析し、どのオフセットにポインタ値が格納されているかを特定します。この情報に基づいて、FUNCDATA_GCArgs
と FUNCDATA_GCLocals
に対応するビットマップデータを生成し、最終的なバイナリの funcdata
セクションに埋め込みます。
src/cmd/cc/pgen.c
およびsrc/cmd/gc/pgen.c
: これらのファイルは、コンパイラのバックエンドの一部であり、コード生成とGCメタデータの生成を担当します。FUNCDATA_GCArgs
とFUNCDATA_GCLocals
という新しいFUNCDATA
エントリが導入されました。gclocalssym
という新しいシンボルが導入され、ローカル変数のビットマップデータを格納します。dumpgcargs
とdumpgclocals
という関数が追加され、それぞれ引数とローカル変数のポインタビットマップを計算し、対応するシンボルにダンプします。pointermap
関数は、引数のみのビットマップ生成に特化するように変更されました。- ローカル変数のサイズを負の値で表現する新しいメカニズムが導入されました。これは、
FUNCDATA_GCLocals
のビットマップデータが、ビットマップ自体ではなく、ローカル変数の総サイズを示す場合があることを意味します。これは、ローカル変数にポインタが含まれていない場合や、特定の最適化が適用される場合に、ビットマップの代わりにサイズ情報のみを格納することで、メタデータのオーバーヘッドを削減するためのものです。
ランタイム (src/pkg/runtime/mgc0.c
) の役割:
ランタイムのGCは、スタックフレームをスキャンする際に、runtime·funcdata
を使用して、現在スキャンしている関数の FUNCDATA_GCArgs
と FUNCDATA_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_GCArgs
とFUNCDATA_GCLocals
の導入: 以前はFUNCDATA_GC
という単一の定数でGC関連のデータブロックを指していましたが、この変更により、引数とローカル変数でそれぞれ異なるビットマップを指すFUNCDATA_GCArgs
とFUNCDATA_GCLocals
が導入されました。これにより、GCメタデータの粒度が向上し、より正確なスキャンが可能になります。 -
gclocalssym
の導入: ローカル変数のポインタビットマップを格納するための新しいシンボルgclocalssym
が導入されました。これは、各関数に対して一意に生成されます。 -
dumpgcargs
とdumpgclocals
関数の追加:dumpgcargs
は関数の引数領域のポインタビットマップを計算し、dumpgclocals
はローカル変数領域のポインタビットマップを計算します。これらの関数は、スタックフレーム内の各変数について、それがポインタであるかどうかを判断し、ビットマップを構築します。 特にdumpgclocals
では、ローカル変数にポインタが含まれている場合にのみwalktype1
を呼び出してビットマップを生成します。また、gextern(gclocalssym, nodconst(-stkoff), off, 4);
のように、stkoff
(スタックフレームのサイズ) を負の値で格納するロジックが追加されています。これは、ローカル変数にポインタがない場合など、ビットマップを生成する必要がない場合に、ローカル変数の総サイズをGCに伝えるための最適化です。 -
gcsymbol
関数の削除: 以前のgcsymbol
関数は、引数とローカル変数の両方のGC情報を単一のシンボルにまとめていましたが、新しいアプローチでは引数とローカル変数が分離されたため、この関数は不要となり削除されました。
src/pkg/runtime/funcdata.h
の変更点
FUNCDATA_GCArgs
とFUNCDATA_GCLocals
の定義:FUNCDATA_GC
がFUNCDATA_GCArgs
とFUNCDATA_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
を取得します。- もし
locals
がnil
であれば(ローつまり、ローカル変数に関するGC情報がない場合)、以前と同様にローカル変数領域全体を保守的にスキャンします。 - もし
locals->n
が負の値であれば、それはローカル変数領域の総サイズを示しているため、そのサイズだけを保守的にスキャンします。これは、ローカル変数にポインタが含まれていない場合の最適化です。 - もし
locals->n
が正の値であれば、それはビットマップの長さを示しているため、scanbitvector
を呼び出して、ローカル変数領域内のポインタを正確にスキャンします。
- もし
- 引数のスキャン:
runtime·funcdata(f, FUNCDATA_GCArgs)
を呼び出して、引数のBitVector
を取得します。- もし
args
がnil
でない、かつargs->n
が正の値であれば、scanbitvector
を呼び出して引数領域内のポインタを正確にスキャンします。 - それ以外の場合は、引数領域全体を保守的にスキャンします。
- もし
- ローカル変数のスキャン:
これらの変更により、GoのGCはスタック上のポインタをより効率的かつ正確に識別できるようになり、GCのパフォーマンスと信頼性が向上しました。
関連リンク
- Go言語のガベージコレクションに関する公式ドキュメント (当時の情報を含む可能性のあるもの):
- Go GC: Prioritizing low latency and simplicity (Go 1.5でのGC改善に関するブログ記事ですが、背景理解に役立つ可能性があります)
- Go runtime documentation (Goランタイムの設計に関する一般的な情報)
参考にした情報源リンク
- Go言語のガベージコレクションに関する一般的な情報源:
- 保守的GCと正確GCに関する一般的な情報:
- Goのコンパイラとランタイムの内部構造に関する情報 (当時のバージョンに特化したものは見つけにくいですが、一般的な理解に役立つもの):
これらの情報は、コミットが行われた2013年当時のGoのGCの状況と、その後の進化を理解する上で役立ちます。