我正在玩红宝石终结者并注意到一些对我来说很奇怪的行为。我可以将触发代码减少到以下内容:
require "weakref"
class Foo
def initialize
ObjectSpace.define_finalizer(self, self.class.finalize)
end
def self.finalize
proc {
puts "finalizing"
}
end
end
Foo.new # does not work
#WeakRef.new(foo) # Using this instead, everything works as expected
sleep 1
ObjectSpace.garbage_collect
puts "... this did not finalize the object"
Foo.new
ObjectSpace.garbage_collect
puts "but this did?"
正如程序所说,在第二次调用Foo.new之前没有运行终结器。我尝试在第一次调用垃圾收集器之前添加更多延迟(虽然据我所知,它根本不是必需的),但这没有做任何事情。
奇怪的是,如果我使用注释掉的行i,第一个终结器会被调用,就像我期望的那样。在程序退出之前,仍未调用第二个。
任何人都可以解释为什么会这样吗?我正在使用ruby 1.9.3p194(2012-04-20修订版35410)[x86_64-linux]运行Ubuntu 12.10。我尝试读取weakref代码,但据我所知,它所做的只是存储对象object_id以便以后检索它。
编辑: 我知道在这种情况下手动调用垃圾收集器是没有意义的。我只是想了解这背后的机制。
答案 0 :(得分:3)
您无法收集Foo
引用,因为它已在您的终结器中引用!因此,因为终结器本身持有对象的引用,所以GC从不收集它,因此从不触发终结器。您只需使用WeakRef作为终结器本身就可以解决这个问题:
require "weakref"
class Foo
class << self
attr_accessor :objects_finalized
def finalize
proc {
@objects_finalized ||= 0
@objects_finalized += 1
}
end
end
def initialize
ObjectSpace.define_finalizer WeakRef.new(self), self.class.finalize
end
end
describe Foo do
it "should be collected" do
Foo.new
expect { GC.start }.to change {
ObjectSpace.each_object(Foo){} }.from(1).to(0)
end
it "should be finalized when it is collected" do
expect { begin; Foo.new; end; GC.start }.to change {
Foo.objects_finalized }.from(nil).to(1)
end
end
结果:
% rspec weakref.rb
..
Finished in 0.03322 seconds
2 examples, 0 failures
答案 1 :(得分:1)
我在http://edwinmeyer.com/Release_Integrated_RHG_09_10_2008/chapter05.html找到了答案(搜索“寄存器和堆栈”)
因为对象的引用仍然存储在处理器寄存器中,所以垃圾收集器保持安全并假设它仍然存活。
答案 2 :(得分:0)
请记住,与Objective-C或C ++等语言不同,只要对对象的所有引用都消失,它就会消失,Ruby就是一种垃圾收集语言。解释器没有理由为一个对象调用庞大的低效垃圾收集器。当垃圾收集器运行时,所有其他处理停止。这是一个很大的表现。解释器非常聪明,可以等到收集之前大部分垃圾都用完了。
示例:你带垃圾桶和垃圾桶一起扔垃圾袋吗?不,你等到它满了,然后去。
如果要强制GC集合,请尝试GC.garbage_collect
手动调用收集器。除非你有充分的理由,否则不要在生产中使用它。