Illustration created for “A Journey With Go”, made from the original Go Gopher, created by Renee French.

本文基于 Go 1.13。

在 Go 中,我们不需要自己管理内存分配和释放。然而,有些时候我们需要对程序进行更细粒度的控制。Go 运行时提供了很多种控制运行时状态及其与内存管理器之间相互影响的方式。本文中,我们来审查让变量不被 GC 回收的能力。

场景

我们来看一个基于 Go 官方文档[1] 的例子:

源码地址[2]

这个程序中一个函数打开文件,另一个函数读取文件。代表文件的结构体注册了一个 finalizer,在 gc 释放结构体时自动关闭文件。运行这个程序,会出现 panic:

下面是流程图:

  • 打开文件,返回一个文件描述符
  • 这个文件描述符被传递给读取文件的函数
  • 这个函数首先做一些繁重的工作:

图 01

allocate 函数触发 gc:

*File*

然后,主协程读取文件:

因为文件已经被 finalizer 关闭,所以会出现 panic。

让变量不被回收

runtime
keepAlivep

追本逐源

keepAlive
keepAlive

在生成的 SSA 代码中也可以看到这个 SSA 指令:

在我的文章 Go 编译器概述[3] 中你可以看到更多关于 Go 编译器和 SSA 的信息。


作者:Vincent Blanchon[4]译者:lxbwolf[5]校对:polaris1119[6]

本文由 GCTT[7] 原创编译,Go 中文网[8] 荣誉推出

参考资料

[1]Go 官方文档:

[2]源码地址:

[3]Go 编译器概述:

[4]Vincent Blanchon:

[5]lxbwolf:

[6]polaris1119:

[7]GCTT:

[8]Go 中文网: