为什么永不调用终结器?

时间:2018-10-22 11:12:33

标签: go finalizer

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

1 个答案:

答案 0 :(得分:3)

  

上面的代码尝试通过SetFinalizer重用对象,但是在调试后我发现永不调用finalizer,为什么?

仅当GC时才在对象上调用终结器 将其标记为未使用,然后尝试在最后进行扫描(免费) GC周期的时间。

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

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

关于终结器的几个注释:

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

    一个必然的结论是不能保证终结器 完全可以运行。

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

    仅在下一个GC周期释放对象本身- 浪费内存。

总而言之,您似乎正在尝试实现析构函数。 请不要:让您的对象实现称为Close的排序标准方法,并在您的类型的约定中声明程序员在完成对象处理后必须调用它。 程序员无论何时想调用这种方法,都使用defer

请注意,此方法适用于Go中的所有类型 stdlib,用于包装OS提供的资源-文件和套接字描述符。因此,无需假装您的类型有所不同。

要记住的另一项有用的事情是,Go的设计经过了精心设计,可以做到无意义,简洁,无魔术,面对面的语言,而您只是在尝试给它增添魔力。 请不要,喜欢解密魔术层的人会使用 Scala 不同的语言进行编程。