go - 为什么永远不会调用终结器?
问题描述
var p = &sync.Pool{
New: func() interface{} {
return &serveconn{}
},
}
func newServeConn() *serveconn {
sc := p.Get().(*serveconn)
runtime.SetFinalizer(sc, (*serveconn).finalize)
fmt.Println(sc, "SetFinalizer")
return sc
}
func (sc *serveconn) finalize() {
fmt.Println(sc, "finalize")
*sc = serveconn{}
runtime.SetFinalizer(sc, nil)
p.Put(sc)
}
上面的代码试图通过 重用对象SetFinalizer
,但是调试后我发现 finalizer 从来没有被调用过,为什么?
更新
解决方案
上面的代码试图通过 重用对象
SetFinalizer
,但是调试后我发现 finalizer 从来没有被调用过,为什么?
仅当 GC 将对象标记为未使用并在 GC 周期结束时尝试清除(空闲)对象时,才会在对象上调用终结器。
作为推论,如果在程序运行期间从未执行 GC 循环,则您设置的终结器可能永远不会被调用。
以防你对 Go 的 GC 有错误的假设,值得注意的是 Go 没有对值使用引用计数。相反,它使用与程序并行工作的 GC,其工作期间的会话定期发生,并由某些参数触发,例如分配产生的堆压力。
关于终结器的一些分类说明:
当程序终止时,不会强制运行 GC。
一个推论是终结器根本不能保证运行。
如果 GC 在即将被释放的对象上找到终结器,它会调用终结器但不会释放该对象。
对象本身只会在下一个 GC 循环中被释放——浪费内存。
总而言之,您似乎试图实现析构函数。请不要:让您的对象实现调用的那种标准方法Close
,并在您的类型的合同中声明程序员在完成对象后需要调用它。当程序员无论如何都想调用这样的方法时,他们使用defer
.
请注意,这种方法非常适用于 Go stdlib 中的所有类型,这些类型包装了操作系统提供的资源——文件和套接字描述符。所以没有必要假装你的类型有所不同。
要记住的另一件有用的事情是,Go 被明确设计为严肃、简洁、没有魔法、直接的语言,而你只是想给它添加魔法。请不要,那些喜欢解密魔法层的人会用Scala不同的语言进行编程。
推荐阅读
- flutter - 如何更改颤振应用程序图标的背景颜色?
- php - 下载 dompdf 而不是在 codeigniter 中查看
- flipkart-api - Flipkart 使用卖家 API 创建列表
- string - 这个字符串是用什么编码的?
- java - 如何检查一个数字是否在一个范围内
- string - Powershell将字符串转换为整数
- java - 无法在 Java 中参数化 SQl 查询
- amazon-s3 - 来自 python aws-lambda 如何使用 GeoPandas 阅读器读取 S3 存储桶上的文件?
- python - 在 Django 中使用 pk 从 UpdateView 到 DetailView 的success_url
- javascript - 使用javascript调用外部api端点时出错