[インデックス 16902] ファイルの概要
このコミットは、Go言語のランタイムにおける runtime.SetFinalizer
関数の挙動を拡張し、ファイナライザ関数として func(interface{})
型の関数を受け入れられるようにする変更です。これにより、特定の型に縛られずに、より汎用的なファイナライザを設定できるようになります。
コミット
commit 6350e45892b5b0189fe3461ba1e7f530da23ff8f
Author: Pieter Droogendijk <pieter@binky.org.uk>
Date: Mon Jul 29 19:43:08 2013 +0400
runtime: allow SetFinalizer with a func(interface{})
Fixes #5368.
R=golang-dev, dvyukov
CC=golang-dev, rsc
https://golang.org/cl/11858043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/6350e45892b5b0189fe3461ba1e7f530da23ff8f
元コミット内容
runtime: allow SetFinalizer with a func(interface{})
Fixes #5368.
R=golang-dev, dvyukov
CC=golang-dev, rsc
https://golang.org/cl/11858043
変更の背景
Go言語の runtime.SetFinalizer
関数は、ガベージコレクタがオブジェクトが到達不能になったと判断した際に実行されるファイナライザ関数を設定するために使用されます。この関数は、ファイルディスクリプタやネットワーク接続など、メモリ以外のリソースを解放する際に特に有用です。
このコミット以前は、SetFinalizer
に渡すファイナライザ関数は、対象となるオブジェクトと厳密に同じ型の引数を1つだけ取る必要がありました。例えば、*int
型のオブジェクトにファイナライザを設定する場合、ファイナライザ関数も func(*int)
のように *int
型の引数を取る必要がありました。
しかし、この厳密な型制約は、異なる型のオブジェクトに対して共通のファイナライザロジックを適用したい場合に不便でした。例えば、複数の異なる構造体に対して、共通のクリーンアップ処理を行いたい場合、それぞれの型に対応するファイナライザ関数を個別に定義する必要がありました。
このコミットは、この制約を緩和し、ファイナライザ関数が func(interface{})
型の引数を受け入れられるようにすることで、より柔軟なファイナライザの定義を可能にすることを目的としています。これにより、ファイナライザ内で interface{}
型の引数を型アサーションすることで、様々な型のオブジェクトに対応できるようになります。
コミットメッセージにある Fixes #5368
は、この変更がGoのIssueトラッカーにおける特定の課題を解決したことを示唆しています。Web検索では直接的なIssue #5368の詳細は見つかりませんでしたが、runtime.SetFinalizer
の柔軟性に関する議論はGoコミュニティで度々行われており、この変更はそのような背景から生まれたものと考えられます。
前提知識の解説
Go言語のガベージコレクション (GC)
Go言語は、自動メモリ管理のためにガベージコレクタ (GC) を採用しています。開発者は手動でメモリを解放する必要がなく、GCが不要になったメモリを自動的に回収します。GoのGCは並行・並行型で、プログラムの実行と同時に動作し、アプリケーションの停止時間を最小限に抑えるように設計されています。
runtime.SetFinalizer
runtime.SetFinalizer(obj, finalizer)
は、Goの組み込み関数で、obj
がガベージコレクタによって到達不能と判断された際に finalizer
関数を実行するように登録します。
obj
: ファイナライザを設定するオブジェクトへのポインタ。new
で割り当てられたオブジェクト、または複合リテラルのアドレスである必要があります。finalizer
:obj
が到達不能になったときに実行される関数。この関数はobj
と同じ型の引数を1つ取ります。
ファイナライザは、主にファイルディスクリプタ、ネットワーク接続、C言語のライブラリによって割り当てられたメモリなど、GoのGCが直接管理できない外部リソースのクリーンアップに使用されます。
注意点:
- ファイナライザの実行タイミングは保証されません。GCの実行に依存するため、プログラム終了前に必ず実行されるとは限りません。
- ファイナライザは別のゴルーチンで実行されます。
- ファイナライザ内でオブジェクトへの参照を保持すると、そのオブジェクトのGCが遅延する可能性があります。
- 1つのオブジェクトには1つのファイナライザしか設定できません。
interface{}
(空インターフェース)
Go言語における interface{}
は「空インターフェース」と呼ばれ、メソッドを一切持たないインターフェースです。Goのすべての型は、少なくとも0個のメソッドを実装しているため、interface{}
はGoのあらゆる型の値を保持できます。これは、他の言語における Object
型や Any
型に似ていますが、Goのインターフェースは静的な型チェックの恩恵を受けつつ、動的な型を扱うための強力なメカニズムを提供します。
interface{}
型の変数に格納された値は、実行時に型アサーション (value.(Type)
) を使用して元の型に変換できます。これにより、異なる型のデータを統一的に扱うことが可能になります。
Goの型システムとランタイム型情報
Goのランタイムは、実行時に型の情報を保持しています。これは、リフレクションやインターフェースの動的な挙動を可能にするために不可欠です。Type
構造体は型のメタデータを表し、Kind
フィールドは型の種類(例えば、KindFunc
は関数型、KindInterface
はインターフェース型)を示します。FuncType
は関数型の詳細(引数、戻り値など)を、InterfaceType
はインターフェース型の詳細を保持します。
技術的詳細
このコミットの主要な変更点は、runtime.SetFinalizer
がファイナライザ関数の引数として interface{}
型を受け入れられるように、ランタイムの型チェックロジックを修正したことです。
以前の runtime.SetFinalizer
の実装では、ファイナライザ関数の唯一の引数の型が、ファイナライズされるオブジェクトの型と厳密に一致することを要求していました。このコミットでは、この型チェックに interface{}
型の引数も許容する条件が追加されました。
具体的には、以下の変更が行われています。
-
runtime.SetFinalizer
の型チェックロジックの変更 (src/pkg/runtime/malloc.goc
):- ファイナライザ関数の引数が1つであること (
ft->in.len != 1
) は引き続きチェックされます。 - 以前は
*(Type**)ft->in.array != obj.type
という条件で、ファイナライザの引数型がオブジェクトの型と一致しない場合にエラーとしていました。 - このコミットでは、この条件が以下のように変更されました:
fint = *(Type**)ft->in.array; if(fint->kind == KindInterface && ((InterfaceType*)fint)->mhdr.len == 0) ot = (PtrType*)obj.type; else if(fint != obj.type) goto badfunc;
fint
はファイナライザ関数の引数型です。fint->kind == KindInterface && ((InterfaceType*)fint)->mhdr.len == 0
の条件は、fint
が空インターフェース (interface{}
) であることをチェックしています。mhdr.len == 0
は、インターフェースがメソッドを持たないことを意味します。- もし引数型が
interface{}
であれば、ot
(original type) にオブジェクトの型 (obj.type
) を設定します。これは、ファイナライザがinterface{}
を受け取る場合でも、元のオブジェクトの型情報を保持しておく必要があるためです。 - それ以外の場合 (
else if(fint != obj.type)
) は、以前と同様にファイナライザの引数型がオブジェクトの型と一致しない場合にbadfunc
へジャンプし、エラーとします。
- この変更により、ファイナライザ関数が
func(interface{})
の形式であってもSetFinalizer
が成功するようになります。
- ファイナライザ関数の引数が1つであること (
-
ファイナライザ情報の拡張 (
src/pkg/runtime/mfinal.c
,src/pkg/runtime/malloc.h
,src/pkg/runtime/mgc0.c
):- ファイナライザの情報を保持する
struct Fin
にvoid *ot;
(original type) フィールドが追加されました。これは、ファイナライザがinterface{}
を受け取る場合に、元のオブジェクトの型情報を保存するために使用されます。 addfintab
関数やruntime·addfinalizer
関数など、ファイナライザを管理する内部関数にot
引数が追加され、この情報がファイナライザテーブルに保存されるようになりました。runtime·getfinalizer
関数もot
を返すように変更されました。mgc0.c
のhandlespecial
関数やrunfinq
関数では、ファイナライザが実行される際に、このot
情報を使用してinterface{}
型の引数を正しく構築するロジックが追加されました。特にrunfinq
では、ファイナライザに渡すフレームのサイズ計算がsizeof(Eface)
に変更され、Eface
構造体(インターフェースの内部表現)を構築してファイナライザに渡す処理が追加されています。
- ファイナライザの情報を保持する
-
runtime.SetFinalizer
のドキュメント更新 (src/pkg/runtime/extern.go
):SetFinalizer
のコメントが更新され、ファイナライザ関数がx
の型、またはinterface{}
型の引数を受け入れられることが明記されました。
-
テストケースの追加 (
src/pkg/runtime/mfinal_test.go
):TestFinalizerTypeSucceed
は、従来のfunc(*int)
のような厳密な型を持つファイナライザが引き続き機能することを確認します。TestFinalizerInterface
は、func(interface{})
型のファイナライザが*int
型のオブジェクトに対して正しく動作することを確認します。ファイナライザ内でinterface{}
から*int
への型アサーションが行われ、値が正しいことが検証されます。TestFinalizerInterfaceBig
は、より大きな構造体 (bigValue
) に対してfunc(interface{})
型のファイナライザが正しく動作することを確認し、データが破損していないことを検証します。
これらの変更により、runtime.SetFinalizer
はより柔軟になり、開発者は interface{}
を利用して汎用的なファイナライザロジックを記述できるようになりました。
コアとなるコードの変更箇所
src/pkg/runtime/extern.go
SetFinalizer
関数のコメントが更新され、ファイナライザがfunc(interface{})
も受け入れることが明記されました。
--- a/src/pkg/runtime/extern.go
+++ b/src/pkg/runtime/extern.go
@@ -122,8 +122,9 @@ func funcentry_go(*Func) uintptr
// The argument x must be a pointer to an object allocated by
// calling new or by taking the address of a composite literal.
// The argument f must be a function that takes a single argument
-// of x's type and can have arbitrary ignored return values.
-// If either of these is not true, SetFinalizer aborts the program.
+// of x's type or interface{}, and can have arbitrary ignored return
+// values. If either of these is not true, SetFinalizer aborts the
+// program.
//
// Finalizers are run in dependency order: if A points at B, both have
// finalizers, and they are otherwise unreachable, only the finalizer
src/pkg/runtime/malloc.goc
SetFinalizer
関数の内部実装で、ファイナライザ関数の引数型チェックロジックが変更されました。
--- a/src/pkg/runtime/malloc.goc
+++ b/src/pkg/runtime/malloc.goc
@@ -813,11 +815,17 @@ func SetFinalizer(obj Eface, finalizer Eface) {
goto throw;
}
nret = 0;
+ ot = nil;
if(finalizer.type != nil) {
if(finalizer.type->kind != KindFunc)
goto badfunc;
ft = (FuncType*)finalizer.type;
- if(ft->dotdotdot || ft->in.len != 1 || *(Type**)ft->in.array != obj.type)
+ if(ft->dotdotdot || ft->in.len != 1)
+ goto badfunc;
+ fint = *(Type**)ft->in.array;
+ if(fint->kind == KindInterface && ((InterfaceType*)fint)->mhdr.len == 0)
+ ot = (PtrType*)obj.type;
+ else if(fint != obj.type)
goto badfunc;
// compute size needed for return parameters
@@ -828,14 +836,14 @@ func SetFinalizer(obj Eface, finalizer Eface) {
nret = ROUND(nret, sizeof(void*));
}
- if(!runtime·addfinalizer(obj.data, finalizer.data, nret)) {
+ if(!runtime·addfinalizer(obj.data, finalizer.data, nret, ot)) {
runtime·printf("runtime.SetFinalizer: finalizer already set\n");
goto throw;
}
return;
badfunc:
- runtime·printf("runtime.SetFinalizer: second argument is %S, not func(%S)\\n", *finalizer.type->string, *obj.type->string);
+ runtime·printf("runtime.SetFinalizer: second argument is %S, not func(%S) or func(interface{})\\n", *finalizer.type->string, *obj.type->string);
throw:
runtime·throw("runtime.SetFinalizer");
}
src/pkg/runtime/malloc.h
runtime·getfinalizer
とruntime·addfinalizer
関数のシグネチャが変更され、void*
型の引数ot
が追加されました。
--- a/src/pkg/runtime/malloc.h
+++ b/src/pkg/runtime/malloc.h
@@ -480,7 +480,7 @@ int32 runtime·gcprocs(void);
void runtime·helpgc(int32 nproc);
void runtime·gchelper(void);
-bool runtime·getfinalizer(void *p, bool del, FuncVal **fn, uintptr *nret);
+bool runtime·getfinalizer(void *p, bool del, FuncVal **fn, uintptr *nret, void **ot);
void runtime·walkfintab(void (*fn)(void*));
enum
src/pkg/runtime/mfinal.c
struct Fin
にvoid *ot;
フィールドが追加されました。addfintab
、runtime·addfinalizer
、runtime·getfinalizer
関数がot
引数を受け入れるように変更されました。
--- a/src/pkg/runtime/mfinal.c
+++ b/src/pkg/runtime/mfinal.c
@@ -13,6 +13,7 @@ struct Fin
{
FuncVal *fn;
uintptr nret;
+ void *ot;
};
// Finalizer hash table. Direct hash, linear scan, at most 3/4 full.
@@ -42,7 +43,7 @@ static struct {
} fintab[TABSZ];
static void
-addfintab(Fintab *t, void *k, FuncVal *fn, uintptr nret)
+addfintab(Fintab *t, void *k, FuncVal *fn, uintptr nret, void *ot)
{
int32 i, j;
@@ -67,6 +68,7 @@ ret:
t->key[i] = k;
t->val[i].fn = fn;
t->val[i].nret = nret;
+ t->val[i].ot = ot;
}
static bool
@@ -87,6 +89,7 @@ lookfintab(Fintab *t, void *k, bool del, Fin *f)
t->key[i] = (void*)-1;
t->val[i].fn = nil;
t->val[i].nret = 0;
+ t->val[i].ot = nil;
t->ndead++;
}
return true;
@@ -123,7 +126,7 @@ resizefintab(Fintab *tab)
for(i=0; i<tab->max; i++) {
k = tab->key[i];
if(k != nil && k != (void*)-1)
- addfintab(&newtab, k, tab->val[i].fn, tab->val[i].nret);
+ addfintab(&newtab, k, tab->val[i].fn, tab->val[i].nret, tab->val[i].ot);
}
runtime·free(tab->key);
@@ -137,7 +140,7 @@ resizefintab(Fintab *tab)
}
bool
-runtime·addfinalizer(void *p, FuncVal *f, uintptr nret)
+runtime·addfinalizer(void *p, FuncVal *f, uintptr nret, void *ot)
{
Fintab *tab;
byte *base;
@@ -166,7 +169,7 @@ runtime·addfinalizer(void *p, FuncVal *f, uintptr nret)
resizefintab(tab);
}
- addfintab(tab, p, f, nret);
+ addfintab(tab, p, f, nret, ot);
runtime·setblockspecial(p, true);
runtime·unlock(tab);
return true;
@@ -175,7 +178,7 @@ runtime·addfinalizer(void *p, FuncVal *f, uintptr nret)
// get finalizer; if del, delete finalizer.
// caller is responsible for updating RefHasFinalizer (special) bit.
bool
-runtime·getfinalizer(void *p, bool del, FuncVal **fn, uintptr *nret)
+runtime·getfinalizer(void *p, bool del, FuncVal **fn, uintptr *nret, void **ot)
{
Fintab *tab;
bool res;
@@ -189,6 +192,7 @@ runtime·getfinalizer(void *p, bool del, FuncVal **fn, uintptr *nret)
return false;
*fn = f.fn;
*nret = f.nret;
+ *ot = f.ot;
return true;
}
src/pkg/runtime/mfinal_test.go
TestFinalizerTypeSucceed
,TestFinalizerInterface
,TestFinalizerInterfaceBig
の3つのテストケースが追加されました。
--- a/src/pkg/runtime/mfinal_test.go
+++ b/src/pkg/runtime/mfinal_test.go
@@ -9,8 +9,94 @@ import (
"sync"
"sync/atomic"
"testing"
+ "time"
)
+func TestFinalizerTypeSucceed(t *testing.T) {
+ if runtime.GOARCH != "amd64" {
+ t.Skipf("Skipping on non-amd64 machine")
+ }
+ ch := make(chan bool)
+ func() {
+ v := new(int)
+ *v = 97531
+ runtime.SetFinalizer(v, func(v *int) {
+ if *v != 97531 {
+ t.Errorf("*int in finalizer has the wrong value: %d\n", *v)
+ }
+ close(ch)
+ })
+ v = nil
+ }()
+ runtime.GC()
+ select {
+ case <-ch:
+ case <-time.After(time.Second * 4):
+ t.Errorf("Finalizer set by SetFinalizer(*int, func(*int)) didn't run")
+ }
+}
+
+func TestFinalizerInterface(t *testing.T) {
+ if runtime.GOARCH != "amd64" {
+ t.Skipf("Skipping on non-amd64 machine")
+ }
+ ch := make(chan bool)
+ func() {
+ v := new(int)
+ *v = 97531
+ runtime.SetFinalizer(v, func(v interface{}) {
+ i, ok := v.(*int)
+ if !ok {
+ t.Errorf("Expected *int from interface{} in finalizer, got %v", *i)
+ }
+ if *i != 97531 {
+ t.Errorf("*int from interface{} has the wrong value: %d\n", *i)
+ }
+ close(ch)
+ })
+ v = nil
+ }()
+ runtime.GC()
+ select {
+ case <-ch:
+ case <-time.After(time.Second * 4):
+ t.Errorf("Finalizer set by SetFinalizer(*int, func(interface{})) didn't run")
+ }
+}
+
+type bigValue struct {
+ fill uint64
+ it bool
+ up string
+}
+
+func TestFinalizerInterfaceBig(t *testing.T) {
+ if runtime.GOARCH != "amd64" {
+ t.Skipf("Skipping on non-amd64 machine")
+ }
+ ch := make(chan bool)
+ func() {
+ v := &bigValue{0xDEADBEEFDEADBEEF, true, "It matters not how strait the gate"}
+ runtime.SetFinalizer(v, func(v interface{}) {
+ i, ok := v.(*bigValue)
+ if !ok {
+ t.Errorf("Expected *bigValue from interface{} in finalizer, got %v", *i)
+ }
+ if i.fill != 0xDEADBEEFDEADBEEF && i.it != true && i.up != "It matters not how strait the gate" {
+ t.Errorf("*bigValue from interface{} has the wrong value: %d\n", *i)
+ }
+ close(ch)
+ })
+ v = nil
+ }()
+ runtime.GC()
+ select {
+ case <-ch:
+ case <-time.After(time.Second * 4):
+ t.Errorf("Finalizer set by SetFinalizer(*bigValue, func(interface{})) didn't run")
+ }
+}
+
func fin(v *int) {
}
src/pkg/runtime/mgc0.c
struct Finalizer
にPtrType *ot;
フィールドが追加されました。handlespecial
関数でruntime·getfinalizer
の呼び出しに&ot
が追加されました。runfinq
関数で、ファイナライザに渡す引数の構築ロジックが変更され、interface{}
型の引数に対応する処理が追加されました。
--- a/src/pkg/runtime/mgc0.c
+++ b/src/pkg/runtime/mgc0.c
@@ -109,6 +109,7 @@ struct Finalizer
FuncVal *fn;
void *arg;
uintptr nret;
+ PtrType *ot;
};
typedef struct FinBlock FinBlock;
@@ -1583,10 +1584,11 @@ handlespecial(byte *p, uintptr size)
{
FuncVal *fn;
uintptr nret;
+ PtrType *ot;
FinBlock *block;
Finalizer *f;
-\tif(!runtime·getfinalizer(p, true, &fn, &nret)) {
+\tif(!runtime·getfinalizer(p, true, &fn, &nret, &ot)) {
runtime·setblockspecial(p, false);
runtime·MProf_Free(p, size);
return false;
@@ -1609,6 +1611,7 @@ handlespecial(byte *p, uintptr size)
finq->cnt++;
f->fn = fn;
f->nret = nret;
+ f->ot = ot;
f->arg = p;
runtime·unlock(&finlock);
return true;
@@ -2272,6 +2275,7 @@ runfinq(void)
FinBlock *fb, *next;
byte *frame;
uint32 framesz, framecap, i;
+ Eface *ef;
frame = nil;
framecap = 0;
@@ -2291,7 +2295,7 @@ runfinq(void)
next = fb->next;
for(i=0; i<fb->cnt; i++) {
f = &fb->fin[i];
- framesz = sizeof(uintptr) + f->nret;
+ framesz = sizeof(Eface) + f->nret;
if(framecap < framesz) {
runtime·free(frame);
// The frame does not contain pointers interesting for GC,
@@ -2301,10 +2305,17 @@ runfinq(void)
frame = runtime·mallocgc(framesz, 0, FlagNoPointers|FlagNoInvokeGC);
framecap = framesz;
}
- *(void**)frame = f->arg;
- reflect·call(f->fn, frame, sizeof(uintptr) + f->nret);
+ if(f->ot == nil)
+ *(void**)frame = f->arg;
+ else {
+ ef = (Eface*)frame;
+ ef->type = f->ot;
+ ef->data = f->arg;
+ }
+ reflect·call(f->fn, frame, framesz);
f->fn = nil;
f->arg = nil;
+ f->ot = nil;
}
fb->cnt = 0;
fb->next = finc;
src/pkg/runtime/runtime.h
runtime·addfinalizer
関数のシグネチャが変更され、void*
型の引数ot
が追加されました。
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -808,7 +808,7 @@ uintptr runtime·ifacehash(Iface, uintptr);\n uintptr runtime·efacehash(Eface, uintptr);\n void* runtime·malloc(uintptr size);\n void runtime·free(void *v);\n-bool runtime·addfinalizer(void*, FuncVal *fn, uintptr);\n+bool runtime·addfinalizer(void*, FuncVal *fn, uintptr, void*);\n void runtime·runpanic(Panic*);\n uintptr runtime·getcallersp(void*);\n int32 runtime·mcount(void);\n```
## コアとなるコードの解説
このコミットの核心は、`runtime.SetFinalizer` が `func(interface{})` を受け入れられるようにするための、ランタイム内部の型チェックとファイナライザ実行ロジックの変更です。
1. **`malloc.goc` における型チェックの緩和**:
- 以前は、ファイナライザ関数の引数型が `obj.type` と完全に一致する必要がありました。
- 新しいロジックでは、ファイナライザ関数の引数型 (`fint`) が `KindInterface` であり、かつメソッドを持たない (`mhdr.len == 0`)、つまり `interface{}` である場合に、特別な処理を行います。この場合、`ot` (original type) 変数に `obj.type` を格納します。これは、ファイナライザが `interface{}` を受け取る場合でも、元のオブジェクトの具体的な型情報を保持しておく必要があるためです。
- これにより、`func(interface{})` が有効なファイナライザとして認識されるようになります。
2. **`mfinal.c` におけるファイナライザ情報の拡張**:
- `struct Fin` は、登録されたファイナライザの情報を保持する構造体です。ここに `void *ot;` フィールドが追加されました。この `ot` は、ファイナライザが `interface{}` を受け取る場合に、元のオブジェクトの具体的な型情報(`obj.type`)を指すポインタとして使用されます。
- `addfintab` や `runtime·addfinalizer` といったファイナライザの登録・管理を行う関数群も、この `ot` を引数として受け取り、`struct Fin` に保存するように変更されました。これにより、ファイナライザが実行される際に、元のオブジェクトの型情報を参照できるようになります。
3. **`mgc0.c` におけるファイナライザ実行時の引数構築**:
- `runfinq` 関数は、ガベージコレクション後にファイナライザを実行する役割を担っています。
- 以前は、ファイナライザに渡す引数フレームは単純にオブジェクトのデータポインタ (`f->arg`) を格納していました。
- 新しいロジックでは、`f->ot` (original type) が `nil` でない場合(つまり、ファイナライザが `interface{}` を受け取るように登録された場合)、`Eface` 構造体(Goのインターフェースの内部表現)を構築します。
- `ef->type = f->ot;`:`Eface` の型情報部分に、保存しておいた元のオブジェクトの型 (`ot`) を設定します。
- `ef->data = f->arg;`:`Eface` のデータ部分に、元のオブジェクトのデータポインタ (`f->arg`) を設定します。
- このようにして構築された `Eface` 構造体が、ファイナライザ関数の `interface{}` 引数として渡されます。これにより、ファイナライザ内で `v.(Type)` のように型アサーションを行うことで、元のオブジェクトの具体的な型にアクセスできるようになります。
- ファイナライザに渡すフレームのサイズも、`sizeof(uintptr)` から `sizeof(Eface)` に変更され、インターフェースの内部表現を格納できる十分な領域が確保されるようになりました。
これらの変更により、Goランタイムは `runtime.SetFinalizer` に対して `func(interface{})` を受け入れるための内部的なメカニズムを確立し、より柔軟なファイナライザの利用を可能にしました。
## 関連リンク
- Go CL 11858043: [https://golang.org/cl/11858043](https://golang.org/cl/11858043)
- GitHubコミット: [https://github.com/golang/go/commit/6350e45892b5b0189fe3461ba1e7f530da23ff8f](https://github.com/golang/go/commit/6350e45892b5b0189fe3461ba1e7f530da23ff8f)
## 参考にした情報源リンク
- `runtime.SetFinalizer` のドキュメントと考慮事項:
- [https://go.dev/pkg/runtime/#SetFinalizer](https://go.dev/pkg/runtime/#SetFinalizer)
- [https://victoriametrics.com/blog/go-finalizers/](https://victoriametrics.com/blog/go-finalizers/)
- Goのガベージコレクションに関する情報:
- [https://github.com/golang/go/issues/68398](https://github.com/golang/go/issues/68398)
- [https://github.com/golang/go/issues/45581](https://github.com/golang/go/issues/45581)
- Go 1.24で導入された `runtime.AddCleanup` について (SetFinalizerの代替):
- [https://go.dev/blog/go1.24-cleanup](https://go.dev/blog/go1.24-cleanup)
- [https://github.com/golang/go/issues/63987](https://github.com/golang/go/issues/63987)
- Goのインターフェースの内部表現に関する情報:
- [https://go.dev/blog/laws-of-reflection](https://go.dev/blog/laws-of-reflection)
- [https://research.swtch.com/interfaces](https://research.swtch.com/interfaces)