go - 垃圾收集器和延迟函数之间的冲突?

标签 go garbage-collection

考虑以下代码片段:

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

这段代码是合法的,可以正常工作。从 a() 返回时文件将被关闭 但是,以下将无法正常工作:

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 的事实,偶尔会收到错误错误的文件描述符。 在垃圾收集期间,它将关闭文件本身。

我不清楚的是,延迟函数仍然有对该文件的引用,所以理论上,它不应该被垃圾回收。 那么为什么 golang 运行时会这样呢?

最佳答案

代码的问题是在 file.Fd() 返回后,file 不可达,所以 file 可能被终结器关闭了(收集垃圾)。

根据 runtime.SetFinalizer :

For example, if p points to a struct that contains a file descriptor d, and p has a finalizer that closes that file descriptor, and if the last use of p in a function is a call to syscall.Write(p.d, buf, size), then p may be unreachable as soon as the program enters syscall.Write. The finalizer may run at that moment, closing p.d, causing syscall.Write to fail because it is writing to a closed file descriptor (or, worse, to an entirely different file descriptor opened by a different goroutine). To avoid this problem, call runtime.KeepAlive(p) after the call to syscall.Write.

runtime.KeepAlive用法:

KeepAlive marks its argument as currently reachable. This ensures that the object is not freed, and its finalizer is not run, before the point in the program where KeepAlive is called.

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)
    }()
 }

关于go - 垃圾收集器和延迟函数之间的冲突?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43861492/

相关文章:

go - 如何在生成交互式程序后捕获/记录所有内容

go - pubsub.NewClient 卡在开发机器和 docker 中

go - 如何在 go 中使用模板进行排名

javascript - 如何在 Spidermonkey 中创建、处理和销毁 JS::Heap<T> 对象?

ruby - 如何分析 Ruby 中的垃圾回收

java - 在标记-清除-压缩的紧凑阶段之后,空闲内存块是否会进入伊甸园?

java - 是否可以将 'see' 对象图用于垃圾回收?

go - Go的 `flag`包打印使用可以吗?

google-app-engine - GAE/Go项目的测试代码放在哪里?

.net - .NET 垃圾收集器在使用调试符号发布版本时是否不那么激进?