首页 > 解决方案 > 为什么永远不会调用终结器?

问题描述

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

标签: gofinalizer

解决方案


上面的代码试图通过 重用对象SetFinalizer,但是调试后我发现 finalizer 从来没有被调用过,为什么?

仅当 GC 将对象标记为未使用并在 GC 周期结束时尝试清除(空闲)对象时,才会在对象上调用终结器。

作为推论,如果在程序运行期间从未执行 GC 循环,则您设置的终结器可能永远不会被调用。

以防你对 Go 的 GC 有错误的假设,值得注意的是 Go 没有对值使用引用计数。相反,它使用与程序并行工作的 GC,其工作期间的会话定期发生,并由某些参数触发,例如分配产生的堆压力。

关于终结器的一些分类说明:

  • 当程序终止时,不会强制运行 GC。

    一个推论是终结器根本不能保证运行。

  • 如果 GC 在即将被释放的对象上找到终结器,它会调用终结器但不会释放该对象。

    对象本身只会在下一个 GC 循环中被释放——浪费内存。

总而言之,您似乎试图实现析构函数。请不要:让您的对象实现调用的那种标准方法Close,并在您的类型的合同中声明程序员在完成对象后需要调用它。当程序员无论如何都想调用这样的方法时,他们使用defer.

请注意,这种方法非常适用于 Go stdlib 中的所有类型,这些类型包装了操作系统提供的资源——文件和套接字描述符。所以没有必要假装你的类型有所不同。

要记住的另一件有用的事情是,Go 被明确设计为严肃、简洁、没有魔法、直接的语言,而你只是想给它添加魔法。请不要,那些喜欢解密魔法层的人会用Scala不同的语言进行编程。


推荐阅读