当最后一个引用被垃圾收集时,或者当任何引用被垃圾收集时,是否会调用终结器?

时间:2017-11-23 06:45:59

标签: julia finalizer

julia docs说:

  

终结者(x,f)

     

当没有时,注册要调用的函数f(x)   程序可访问的x引用。 x的类型必须是可变的   struct,否则此函数的行为是不可预测的。

然而,我似乎在观察他们在我的类型的第一次引用丢失其最后一个引用时触发。

考虑:

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

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

我原以为:

  • a更改为其他内容时,没有任何反应
  • b也被更改为其他内容时,两个注册finalizers都会触发。导致" a"和" b"两者都要添加到fin_calls数组中。

相反,我所观察的是:

我原以为:

  • a更改为其他内容时,两个注册finalizers都会触发。导致" a"和" b"两者都要添加到fin_calls数组中。
  • 如果b也被更改,则不会发生任何其他情况。

了解正在发生的事情的正确方法是什么? 这是在朱莉娅6.0

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

  • 在原始机器上显示为0.6.0。
  • 在另一台机器上:
    • 0.6.1(-O2默认值)导致终结者未被调用失败(可以,终结者被称为迟到)。
    • 0.7-dev(-O0)导致传球,
    • 0.7-dev(-O1或更高)由于被调用而导致失败。

1 个答案:

答案 0 :(得分:2)

如果你不使用变量, 垃圾收集器可以在上次使用后随时收集变量。

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

我相信这就是发生的事情。 如果我添加一些变量用途,行为就像预期的那样。

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从未被调用过。因为垃圾收集器可以选择不运行。

结束