go - 在 Go 中,什么时候变量会变得不可访问?

标签 go garbage-collection

今天早上发布了 Go 1.7 beta 1,这里是 the release notes draft of Go 1.7 .一个新函数 KeepAlive 被添加到包 runtime 中。 The doc of runtime.KeepAlive给出了一个例子:

type File struct { d int }
d, err := syscall.Open("/file/path", syscall.O_RDONLY, 0)
// ... do something if err != nil ...
p := &FILE{d}
runtime.SetFinalizer(p, func(p *File) { syscall.Close(p.d) })
var buf [10]byte
n, err := syscall.Read(p.d, buf[:])
// Ensure p is not finalized until Read returns.
runtime.KeepAlive(p)
// No more uses of p after this point.

The doc of runtime.SetFinalizer也给出了关于runtime.KeepAlive的解释:

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.

让我疑惑的是变量p还没有离开它的生命范围,为什么会不可达呢?这是否意味着只要在下面的代码中没有使用变量,无论它是否在其生命范围内,它都是不可访问的?

最佳答案

当运行时检测到 Go 代码无法到达再次引用该变量的点时,该变量变得无法访问。

在您发布的示例中,一个 syscall.Open()用于打开文件。返回的文件描述符(只是一个 int 值)被“包装”在 struct 中。然后将终结器附加到关闭文件描述符的结构值。现在,当这个结构值变得不可访问时,它的终结器可能会随时运行,并且文件描述符的关闭/无效/重新使用可能会导致意外行为或 Read() 执行中的错误> 系统调用。

Go 代码中最后一次使用此结构值 p 是在调用 syscall.Read() 时(并且文件描述符 p.d 传递给它)。系统调用的实现将在 syscall.Read()启动 之后使用该文件描述符,它可能会一直这样做直到 syscall.Read()返回。但是这种文件描述符的使用是“独立”于 Go 代码的。

所以结构值p在系统调用的执行过程中没有被使用,系统调用阻塞Go代码直到它返回。这意味着 Go 运行时可以在 Read() 执行期间(在 Read() 返回之前)将 p 标记为不可访问,或者 < em>甚至在其实际执行开始之前(因为p 仅用于提供调用Read() 的参数。

因此调用 runtime.KeepAlive() : 由于此调用是 syscall.Read() 并且它引用 变量 p,因此 Go 运行时不允许在 Read() 返回之前将 p 标记为不可访问,因为这是在 Read() 调用之后。

请注意,您可以使用其他结构来“保持 p 存活”,例如_ = p 或返回它。 runtime.KeepAlive() 在后台没有做任何神奇的事情,它的实现是:

func KeepAlive(interface{}) {}

runtime.KeepAlive() 确实提供了一个更好的选择,因为:

  • 它清楚地记录了我们希望保持 p 事件(以防止运行 Finalizers )。
  • 使用其他构造,例如 _ = p 可能会被 future 的编译器“优化”,但不会被 runtime.KeepAlive() 调用。

关于go - 在 Go 中,什么时候变量会变得不可访问?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37588639/

相关文章:

java - GWT 垃圾收集

go - 包含许多文件的 golang 包的内部组织

go - 如何设置用于 HTTP 请求的 IP?

go - 使用 base64.StdEncoding.DecodeString(str) 时输入字节 4 处的非法 base64 数据

java - 如何摆脱java中对象的实例?

android - 滚动 ListView 时有很多 GC(使用 holder 模式)

Java 堆栈垃圾收集器

javascript - 如何理解 Node js 进程崩溃内存不足的日志

go - 任何有关如何使用Go作为身份验证服务的一部分为Google provider实现OIDC实现的想法

elasticsearch - 我们应该在应用程序中创建多少个 elasticsearch 客户端连接