From: David Masover Date: 2009-07-23T13:44:30+09:00 Subject: Garbage collection oddities Given this code: class WatchMeDie def self.finalizer proc do |id| puts "Finalized #{id}" end end def initialize ObjectSpace.define_finalizer self, &WatchMeDie.finalizer end end a = WatchMeDie.new a = nil GC.start puts 'end of program' # # # This produces the expected outcome on 1.8.7: Finalized 69858803067800 end of program On 1.9.1, however, it doesn't: end of program Finalized 8054060 That is, on 1.9.1, the object is never collected. However, if I create two such objects: a = WatchMeDie.new b = WatchMeDie.new a = nil b = nil GC.start Once I do this, the first object is collectible. It seems the most recent WatchMeDie object will never be collected before the program ends. Not that any of this really matters, but it complicates what I was actually testing: def make_a_thread Thread.new do sleep 1000 end end def test_run make_a_thread WatchMeDie.new end a = test_run b = WatchMeDie.new a = nil GC.start puts 'end of program' This example works as expected -- one WatchMeDie is finalized before program end, and one after. However, if we change test_run to: def test_run make_a_thread foo = WatchMeDie.new end 1.9.1 still works as expected. However, 1.8 does not -- because I've now assigned that object to a local variable, even one that didn't exist when I called make_a_thread, it now can't be garbage collected. One possible workaround, doesn't: MyThreadProc = proc do sleep 1000 end def make_a_thread Thread.new &MyThreadProc end In other words, for some bizarre reason, either the thread or the proc still cares enough about that local scope, and the scope of all its callers, to hold onto them for posterity. I've worked around this in 1.8 by sending the thread spawning itself to another thread, one created with a somewhat cleaner scope and call stack. But it's a brutally ugly hack, and there have to be performance implications. Fortunately, 1.9.1 appears to do the right thing, here. Unfortunately, this kind of stuff is difficult to test. So I'm curious: Is this unique to 1.8.7, or does it exist in 1.8.6, also? And is it likely to be fixed, or should I just strongly encourage people to upgrade to 1.9?