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,为什么?
更新
这可能与以下内容有关:https://github.com/golang/go/issues/2368
答案 0 :(得分:3)
上面的代码尝试通过
SetFinalizer
重用对象,但是在调试后我发现永不调用finalizer,为什么?
仅当GC时才在对象上调用终结器 将其标记为未使用,然后尝试在最后进行扫描(免费) GC周期的时间。
作为必然结果,如果在程序运行时从未执行过GC循环,则可能永远不会调用您设置的终结器。
以防万一您对Go的GC有错误的假设,可能值得注意的是Go并未使用对数值的引用计数。取而代之的是,它使用与程序并行工作的GC,并且它的工作周期是定期发生的,并由某些参数触发,例如分配产生的堆压力。
关于终结器的几个注释:
程序终止时,不会强制运行GC。
一个必然的结论是不能保证终结器 完全可以运行。
如果GC在即将释放的对象上找到了终结器, 它调用终结器,但不会释放对象。
仅在下一个GC周期释放对象本身- 浪费内存。
总而言之,您似乎正在尝试实现析构函数。
请不要:让您的对象实现称为Close
的排序标准方法,并在您的类型的约定中声明程序员在完成对象处理后必须调用它。
程序员无论何时想调用这种方法,都使用defer
。
请注意,此方法适用于Go中的所有类型 stdlib,用于包装OS提供的资源-文件和套接字描述符。因此,无需假装您的类型有所不同。
要记住的另一项有用的事情是,Go的设计经过了精心设计,可以做到无意义,简洁,无魔术,面对面的语言,而您只是在尝试给它增添魔力。
请不要,喜欢解密魔术层的人会使用 Scala 不同的语言进行编程。