>>>>> "W" == Wincent Colaiuta <win / wincent.com> writes:

W> def parse
W>   # do stuff
W>   cache = MemoizingCache.new
     ^^^^^
W>   $stderr.print "New cache id #{cache.object_id}\n"
W>   ObjectSpace.define_finalizer(cache, lambda { |id| $stderr.print
                                         ^^^^^^
W> "Finalizer for id #{id}\n"})
W>   # do more stuff
W> end

W> And have confirmed that the cache finalizers are not being called. I
W> haven't found out how the reference to the local "cache" variable is
W> being kept alive even after it falls out of scope, but you've given me
W> the tool necessary to at least find out what's happening.

 The closure (lambda) has a reference to the variable cache

 Here a stupid example

moulon% cat b.rb
#!/usr/bin/ruby

class A
   def parse
      cache = Array.new(100)
      ObjectSpace.define_finalizer(cache, lambda {|i| $stderr.puts "obj_id #{i}" })
   end
end

ARGV[0].to_i.times { A.new.parse }
$stderr.puts "gc"
GC.start
$stderr.puts "at end"
moulon% 

moulon% ./b.rb 2
gc
at end
obj_id -605590758
obj_id -605590688
moulon% 

 Now if I change this to

moulon% cat b.rb
#!/usr/bin/ruby

class A
   def self.x
      lambda {|i| $stderr.puts "obj_id #{i}" }
   end

   def parse
      cache = Array.new(100)
      ObjectSpace.define_finalizer(cache, A.x)
   end
end

ARGV[0].to_i.times { A.new.parse }
$stderr.puts "gc"
GC.start
$stderr.puts "at end"
moulon% 

moulon% ./b.rb 2
gc
obj_id -605498718
at end
obj_id -605498798
moulon%

 only one object is freed when GC.start run and you can see it with this case

moulon% ./b.rb 1
gc
at end
obj_id -605640028
moulon% 


 ruby call the finalizer at the end only, because when GC.start run it
 find a reference to the object (in cache) on the stack and mark it


Guy Decoux