D
David Masover
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?
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?