On Tue, Jan 24, 2006 at 03:02:04AM +0900, James Edward Gray II wrote: > The following code, adapted from an old post by Guy Decoux seems to > do the trick: > > class WeakCache > def initialize( cache = Hash.new ) > @cache = cache > end [...] > def []=( key, value ) > ObjectSpace.define_finalizer(value, lambda { @cache.delete(key) }) ============================== This keeps a reference to value so it will never be reclaimed: class WeakCache attr_reader :cache def initialize( cache = Hash.new ) @cache = cache end def []( key ) value_id = @cache[key] return ObjectSpace._id2ref(value_id) unless value_id.nil? nil end def []=( key, value ) ObjectSpace.define_finalizer(value, lambda { @cache.delete(key) }) @cache[key] = value.object_id end end RUBY_VERSION # => "1.8.4" RUBY_RELEASE_DATE # => "2005-12-24" c = WeakCache.new puts c.cache.size 100000.times{|i| c[i] = i.to_s} GC.start puts c.cache.size __END__ # >> 0 # >> 100000 Compare to this: class WeakCache attr_reader :cache def initialize( cache = Hash.new ) @cache = cache end def []( key ) value_id = @cache[key] return ObjectSpace._id2ref(value_id) unless value_id.nil? nil end def make_lambda(key) lambda{|value| @cache.delete(key) } end def []=( key, value ) ObjectSpace.define_finalizer(value, make_lambda(key)) @cache[key] = value.object_id end end RUBY_VERSION # => "1.8.4" RUBY_RELEASE_DATE # => "2005-12-24" c = WeakCache.new puts c.cache.size 100000.times{|i| c[i] = i.to_s} GC.start puts c.cache.size __END__ # >> 0 # >> 3 That is very inefficient though, you want something more like class WeakCache attr_reader :cache def initialize( cache = Hash.new ) @cache = cache @rev_cache = {} @reclaim_method = method(:reclaim_value) end def []( key ) value_id = @cache[key] return ObjectSpace._id2ref(value_id) unless value_id.nil? nil end def reclaim_value(value_id) @cache.delete @rev_cache[value_id] @rev_cache.delete value_id end def []=( key, value ) @rev_cache[value.object_id] = key @cache[key] = value.object_id ObjectSpace.define_finalizer(value, @reclaim_method) end end RUBY_VERSION # => "1.8.4" RUBY_RELEASE_DATE # => "2005-12-24" c = WeakCache.new puts c.cache.size 100000.times{|i| c[i] = i.to_s} GC.start puts c.cache.size __END__ # >> 0 # >> 4 -- Mauricio Fernandez