I was playing around with ruby finalizers and noticed some behaviour that is very strange to me. I could reduce the triggering code to the following:
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?"
As the program says, no finalizer is run before the second call to Foo.new. I tried adding more delay before the first call to the garbage collector (though as I understand, it shouldn't be neccessary at all), but that doesn't do anything.
Strangely enough, if I use the commented-out line i, the first finalizer gets called as I would expect it to be. The second one is still not called before the program exits.
Can anyone explain why this is happening? I am running Ubuntu 12.10 with ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]. I tried reading the weakref code, but as far as I can tell, all it does is storing the objects object_id to retrieve it later.
edit: I understand that manually invoking the garbage collector in a situation like this does not make sense. I'm just trying to understand the mechanics behind this.
You can't collect your Foo
reference because it is referenced in your finalizer! Thus, because the finalizer itself is holding a reference to the object, the GC never collects it, and thus never triggers the finalizer. You can get around this by just using a WeakRef for the finalizer itself:
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
With results:
% rspec weakref.rb
..
Finished in 0.03322 seconds
2 examples, 0 failures
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With