当最后一个引用被垃圾回收时,或者当 Any 引用被垃圾回收时,是否调用终结器?

Are finalisers called when the last reference is garbage collected, or when Any reference is garbage collected?

julia docs 说:

finalizer(x, f)

Register a function f(x) to be called when there are no program-accessible references to x. The type of x must be a mutable struct, otherwise the behavior of this function is unpredictable.

但是,我似乎观察到它们在对我的类型的第一个引用丢失其最后一个引用时触发。

考虑:

using Base.Test
mutable struct Foo
    val
end
@testset "how do Finalisers work" begin let
    fin_calls = []

    a = Foo(1)
    b = a
    finalizer(a, _ -> push!(fin_calls, "a"))
    finalizer(b, _ -> push!(fin_calls, "b"))

    @test fin_calls == []
    a = 7
    gc()
    @test fin_calls == [] # Fails: Evaluated: Any["a", "b"] == Any[]
    #shouldn't trigger finaliser as still has 1 ref, or so I thought

    b=8
    gc()
    @test Set(fin_calls) == Set(["a", "b"]) #both finalizers should trigger
end end

有 2 个引用我的 Foo(1),分别是 ab

我本以为:

相反,我观察到的是:

我本以为:

理解正在发生的事情的正确方法是什么? 这是在 julia 6.0

我重现这个的能力似乎各不相同。

如果不使用变量, 垃圾收集器可以在最后一次使用后的任何时间自由收集变量。

如果您根本不使用变量,那么优化器可以完全不在实际代码中生成它。

我相信这就是发生的事情。 如果我添加更多变量的使用,则行为符合预期。

using Base.Test

"This function makes use of `xs` in a way no optimizer can possibly remove"
function use(xs...)
    mktemp() do fn, fh
        print(fh, xs)
    end
end


mutable struct Foo
    val
end

function foo()
    @testset "how do Finalisers work" begin let
        fin_calls = []

        a = Foo(1)
        b = a
        finalizer(a, _ -> push!(fin_calls, "a"))
        finalizer(b, _ -> push!(fin_calls, "b"))

        use(a,b)
        @test fin_calls == []
        a = 7
        gc()
        use(b)
        @test fin_calls == [] # Fails: Evaluated: Any["a", "b"] == Any[]
        #shouldn't trigger finaliser as still has 1 ref, or so I thought

        b=8
        gc()
        @test Set(fin_calls) == Set(["a", "b"]) #both finalizers should trigger
    end end

这段代码,对我来说进入了最终测试,然后失败了,因为那时 fin_calls 从未被调用过。因为垃圾收集器可以选择不 运行.

结束