2017-05-09 8 views
0

は、次のコードスニペットを考えてみましょう:ガベージコレクタと遅延機能の衝突?

func a(fd int) { 
file := os.NewFile(uintptr(fd), "") 
defer func() { 
    if err := file.Close(); err != nil { 
     fmt.Printf("%v", err) 
    } 
} 

コードのこの作品は合法で、[OK]を動作します。 、

func a(fd int) { 
file := os.NewFile(uintptr(fd), "") 
defer func() { 
    if err := syscall.Close(int(file.Fd()); err != nil { 
     fmt.Printf("%v", err) 
    } 
} 

時折、受信されるエラー原因NewFile setting a finalizer ガベージコレクション中にいる、という事実に、bad file descriptor次のようになります。ファイルはしかし、以下は正しく動作しませんa() からの復帰時に閉じられますファイル自体を閉じます。

私には分かりませんが、遅延関数には依然としてファイルへの参照があるため、理論的にはまだガベージコレクションされるべきではありません。 なぜgolangランタイムはそのように動作しますか?

+0

関連の/可能duplicatr [ゴーでは、とき変数が到達不能になるのだろうか?](http://stackoverflow.com/questions/37588639/in-go-when-will-a-variable-到達不能/ 37591282#37591282) – icza

答えて

4

コードの問題は、file.Fd()が返された後、fileに到達できないため、fileがファイナライザ(ガベージコレクション)によって閉じることがあります。 runtime.SetFinalizerに応じ

:たとえば

、ファイルディスクリプタdを含み、pはそのファイルディスクリプタをクローズファイナライザを持つ構造体へのPポイントの場合、およびAのpの最後の使用であれば関数がsyscall.Write(pd、buf、size)の呼び出しである場合、プログラムがsyscall.Writeに入るとすぐにpに到達できない可能性があります。ファイナライザは、その時点でp.dを閉じて実行され、クローズドファイル記述子に書き込んでいるために失敗します(あるいは、悪いことに、別のゴルーチンによってオープンされた全く異なるファイル記述子に)。この問題を回避するには、syscall.Writeの呼び出しの後にruntime.KeepAlive(p)を呼び出します。

runtime.KeepAlive用法:

キープアライブは、現在、到達可能なように、その引数をマーク。これにより、KeepAliveが呼び出されたプログラム内のポイントの前に、オブジェクトが解放されず、ファイナライザが実行されなくなります。

func a(fd int) { 
    file := os.NewFile(uintptr(fd), "") 
    defer func() { 
     if err := syscall.Close(int(file.Fd()); err != nil { 
      fmt.Printf("%v", err) 
     } 
     runtime.KeepAlive(file) 
    }() 
} 
関連する問題