From: Robert Klemme Date: 2003-08-12T20:43:35+09:00 Subject: Re: WeakRef and caches "Kent Dahl" schrieb im Newsbeitrag news:bh8eed$r82$1@tyfon.itea.ntnu.no... > Tim Bates wrote: > > On Mon, Aug 11, 2003 at 09:56:44PM +0900, Robert Klemme wrote: > >>Your cache implementation is exactly as it should be, but the code outside > >>should only hold on to an instance as long as it is needed, typically a > >>method invocation's duration. The only thing the outside world should store > >>is the id, which is needed to access the instance via the cache. A typical > >>usage pattern looks like this > > > > Yes, except that it doesn't work: > > > > [tim@zaphod:3 ~/ruby]$ cat weakref_test.rb > > require 'weakref' > > > > o = Object.new > > q = o > > o = WeakRef.new(o) > > ObjectSpace.garbage_collect > > p o > > p q > > q = nil # o is now the only reference to the object. > > ObjectSpace.garbage_collect > > puts o > > Playing around I noticed this: > > require 'weakref' > o = WeakRef.new(Object.new) > puts o.__getobj__ # A > puts o.__getobj__.to_s # B > ObjectSpace.garbage_collect > puts o > > Comment out line A and B in turn. With only B, o gets GC'ed, but not > with line A. > > A wild guess: Parameters to method calls are pushed onto the stack and > (naturally for efficiency) not cleaned away after the method call has > ended. Thus the current call frame contains a reference to the > "unreferenced" object, and you cannot be sure that it will disappear > until after the call frame has been "popped" from the call stack. > > Does that make any sense to someone with knowledge of the Ruby innards? A WeakRef does not guarantee that an instance is GC'ed as soon as there is no strong ref any more. It's the other way round: *if* there is no strong ref any more, then the instance *might* be GC'ed during a GC cycle. Introducing another cycle leads to this: irb(main):001:0> require 'weakref' => true irb(main):002:0> irb(main):003:0* o = WeakRef.new(Object.new) => # irb(main):004:0> o.__getobj__ # A => # irb(main):005:0> # o.__getobj__.to_s # B irb(main):006:0* ObjectSpace.garbage_collect => nil irb(main):007:0> o => # irb(main):008:0> ObjectSpace.garbage_collect => nil irb(main):009:0> o WeakRef::RefError: Illegal Reference - probably recycled from C:/Programme/Ruby-1.8.0/lib/ruby/1.8/irb.rb:292:in `output_value' from C:/Programme/Ruby-1.8.0/lib/ruby/1.8/irb.rb:149:in `eval_input' from C:/Programme/Ruby-1.8.0/lib/ruby/1.8/irb.rb:146:in `signal_status' from C:/Programme/Ruby-1.8.0/lib/ruby/1.8/irb.rb:146:in `eval_input' from C:/Programme/Ruby-1.8.0/lib/ruby/1.8/irb.rb:144:in `each_top_level_ statement' from C:/Programme/Ruby-1.8.0/lib/ruby/1.8/irb/ruby-lex.rb:219:in `loop' from C:/Programme/Ruby-1.8.0/lib/ruby/1.8/irb/ruby-lex.rb:247:in `each_t op_level_statement' from C:/Programme/Ruby-1.8.0/lib/ruby/1.8/irb/ruby-lex.rb:218:in `catch' from C:/Programme/Ruby-1.8.0/lib/ruby/1.8/irb/ruby-lex.rb:218:in `each_t op_level_statement' from C:/Programme/Ruby-1.8.0/lib/ruby/1.8/irb.rb:144:in `eval_input' from C:/Programme/Ruby-1.8.0/lib/ruby/1.8/irb.rb:70:in `start' from C:/Programme/Ruby-1.8.0/lib/ruby/1.8/irb.rb:69:in `catch' from C:/Programme/Ruby-1.8.0/lib/ruby/1.8/irb.rb:69:in `start' from C:/Programme/Ruby-1.8.0/bin/irb:13 Maybe IRB bug!! irb(main):010:0> Of course another option is that you might have found a bug in WeakRef / GC, but I don't assume so in this case. Regards robert