为什么永远不会调用终结器?

Why finalizer is never called?

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 重用对象,但调试后我发现终结器从未被调用,为什么?

更新

这可能是相关的:https://github.com/golang/go/issues/2368

The above code tries to reuse object by SetFinalizer, but after debug I found finalizer is never called, why?

finalizer 仅在 GC 时在对象上调用 将其标记为未使用,然后尝试在最后清扫(免费) GC 周期。

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

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

一些关于终结器的注释:

  • 程序终止时,不强制GC运行.

    由此推论,无法保证终结器 完全 运行。

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

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

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

请注意,此方法适用于 Go 中的所有类型 stdlib 包装由 OS 提供的资源——文件和套接字描述符。所以没有必要假装你的类型有什么不同。

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