Mauricio Fernández
1/24/2006 12:01:00 AM
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