github.com/shogo82148/std@v1.22.1-0.20240327122250-4e474527810c/runtime/mfinal.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Garbage collector: finalizers and block profiling.
     6  
     7  package runtime
     8  
     9  // SetFinalizerは、objに関連付けられたファイナライザを提供されたファイナライザ関数に設定します。
    10  // ガベージコレクタが関連付けられたファイナライザを持つ到達不能なブロックを見つけると、
    11  // 関連付けをクリアし、別のゴルーチンでfinalizer(obj)を実行します。
    12  // これにより、objは再び到達可能になりますが、関連付けられたファイナライザはなくなります。
    13  // SetFinalizerが再度呼び出されない限り、次にガベージコレクタがobjが到達不能であることを検出した場合、objは解放されます。
    14  //
    15  // SetFinalizer(obj, nil)は、objに関連付けられたファイナライザをクリアします。
    16  //
    17  // 引数objは、newを呼び出すことによって割り当てられたオブジェクトへのポインタ、
    18  // 複合リテラルのアドレスを取得することによって、またはローカル変数のアドレスを取得することによって割り当てられたオブジェクトへのポインタである必要があります。
    19  // 引数finalizerは、objの型に割り当てることができる単一の引数を取る関数であり、任意の無視される戻り値を持つことができます。
    20  // これらのいずれかがtrueでない場合、SetFinalizerはプログラムを中止する可能性があります。
    21  //
    22  // ファイナライザは依存関係の順序で実行されます。
    23  // AがBを指し示し、両方にファイナライザがあり、それらが到達不能である場合、Aのファイナライザのみが実行されます。
    24  // Aが解放された後、Bのファイナライザが実行されます。
    25  // ファイナライザを持つブロックを含む循環構造がある場合、その循環はガベージコレクトされることは保証されず、
    26  // 依存関係を尊重する順序がないため、ファイナライザが実行されることも保証されません。
    27  //
    28  // ファイナライザは、プログラムがobjが指し示すオブジェクトに到達できなくなった後、
    29  // 任意の時点で実行されるようにスケジュールされます。
    30  // プログラムが終了する前にファイナライザが実行されることは保証されていないため、
    31  // 通常、長時間実行されるプログラム中にオブジェクトに関連付けられた非メモリリソースを解放するためにのみ有用です。
    32  // たとえば、[os.File] オブジェクトは、プログラムがCloseを呼び出さずにos.Fileを破棄するときに、
    33  // 関連するオペレーティングシステムのファイルディスクリプタを閉じるためにファイナライザを使用できますが、
    34  // bufio.WriterのようなインメモリI/Oバッファをフラッシュするためにファイナライザに依存することは誤りです。
    35  // なぜなら、プログラムが終了するときにバッファがフラッシュされないためです。
    36  //
    37  // *objのサイズがゼロバイトの場合、ファイナライザが実行されることは保証されません。
    38  // なぜなら、メモリ内の他のゼロサイズのオブジェクトと同じアドレスを共有する可能性があるためです。
    39  // 詳細については、https://go.dev/ref/spec#Size_and_alignment_guarantees を参照してください。
    40  //
    41  // パッケージレベルの変数の初期化子で割り当てられたオブジェクトに対して、
    42  // ファイナライザが実行されることは保証されません。
    43  // このようなオブジェクトはヒープに割り当てられるのではなく、リンカによって割り当てられる可能性があります。
    44  //
    45  // ファイナライザがオブジェクトが参照されなくなってから任意の時間が経過した後に実行される可能性があるため、
    46  // ランタイムは、オブジェクトを単一の割り当てスロットにまとめるスペース節約の最適化を実行できます。
    47  // そのような割り当て内の参照されなくなったオブジェクトのファイナライザは、常に参照されたオブジェクトと同じバッチに存在する場合、実行されない可能性があります。
    48  // 通常、このバッチ処理は、小さな(16バイト以下の)ポインタフリーオブジェクトに対してのみ行われます。
    49  //
    50  // オブジェクトが到達不能になるとすぐにファイナライザが実行される場合があります。
    51  // ファイナライザを正しく使用するには、プログラムはオブジェクトが不要になるまで到達可能であることを保証する必要があります。
    52  // グローバル変数に格納されたオブジェクト、またはグローバル変数からポインタをトレースできるオブジェクトは到達可能です。
    53  // その他のオブジェクトについては、[KeepAlive] 関数の呼び出しにオブジェクトを渡して、
    54  // オブジェクトが到達可能である必要がある関数内の最後のポイントをマークする必要があります。
    55  //
    56  // たとえば、pがファイルディスクリプタdを含むos.Fileのような構造体を指し示す場合、
    57  // pにはdを閉じるファイナライザがあり、pの最後の使用がsyscall.Write(p.d、buf、size)の呼び出しである場合、
    58  // プログラムがsyscall.Writeに入るとすぐにpが到達不能になる可能性があります。
    59  // その瞬間にファイナライザが実行され、p.dを閉じ、syscall.Writeが閉じられたファイルディスクリプタ(または、
    60  // より悪い場合、別のgoroutineによって開かれた完全に異なるファイルディスクリプタ)に書き込もうとして失敗する可能性があります。
    61  // この問題を回避するには、syscall.Writeの呼び出し後にKeepAlive(p)を呼び出します。
    62  //
    63  // プログラムのすべてのファイナライザを、1つのgoroutineが順次実行します。
    64  // ファイナライザが長時間実行する必要がある場合は、新しいgoroutineを開始することで実行する必要があります。
    65  //
    66  // Goのメモリモデルの用語で、SetFinalizer(x、f)の呼び出しは、
    67  // ファイナライザ呼び出しf(x)の前に「同期」します。
    68  // ただし、KeepAlive(x)またはxの他の使用がf(x)の前に「同期」されることは保証されていないため、
    69  // 一般的には、ファイナライザがxの可変状態にアクセスする必要がある場合は、ミューテックスまたは他の同期メカニズムを使用する必要があります。
    70  // たとえば、x内の時折変更される可変フィールドを検査するファイナライザを考えてみましょう。
    71  // xが到達不能になり、ファイナライザが呼び出される前に、メインプログラムで時折変更される場合。
    72  // メインプログラムでの変更とファイナライザでの検査は、読み書き競合を回避するために、ミューテックスやアトミック更新などの適切な同期を使用する必要があります。
    73  func SetFinalizer(obj any, finalizer any)
    74  
    75  // KeepAliveは、引数を現在到達可能なものとしてマークします。
    76  // これにより、オブジェクトが解放されず、そのファイナライザが実行されないようになります。
    77  // KeepAliveが呼び出されたプログラムのポイントより前に。
    78  //
    79  // KeepAliveが必要な場所を示す非常に簡単な例:
    80  //
    81  //	type File struct { d int }
    82  //	d, err := syscall.Open("/file/path", syscall.O_RDONLY, 0)
    83  //	// ... errがnilでない場合は何かを実行します ...
    84  //	p := &File{d}
    85  //	runtime.SetFinalizer(p, func(p *File) { syscall.Close(p.d) })
    86  //	var buf [10]byte
    87  //	n, err := syscall.Read(p.d, buf[:])
    88  //	// Readが返るまで、pがファイナライズされないようにします。
    89  //	runtime.KeepAlive(p)
    90  //	// このポイント以降、pを使用しないでください。
    91  //
    92  // KeepAlive呼び出しがない場合、ファイナライザは [syscall.Read] の開始時に実行され、
    93  // 実際のシステムコールを行う前にファイルディスクリプタを閉じます。
    94  //
    95  // 注意:KeepAliveは、ファイナライザが予期せず実行されるのを防止するためにのみ使用する必要があります。
    96  // 特に、[unsafe.Pointer] と一緒に使用する場合は、unsafe.Pointerの有効な使用方法のルールが適用されます。
    97  func KeepAlive(x any)