As mentioned in a previous thread on ruby-talk, we are looking to
remove ObjectSpace_id2ref from JRuby. Matz explained that it was added
to support WeakRef, for which we have our own (Java-based)
implementation. Supporting it on the JVM (or any VM where objects move
in memory) is rather expensive.
The only place in stdlib that uses _id2ref_ is this class in drb/drb.rb:
class DRbIdConv
# Convert an object reference id to an object.
···
#
# This implementation looks up the reference id in the local object
# space and returns the object it refers to.
def to_obj(ref)
ObjectSpace._id2ref(ref)
end
# Convert an object into a reference id.
#
# This implementation returns the object's __id__ in the local
# object space.
def to_id(obj)
obj.nil? ? nil : obj.__id__
end
end
I have come up with a possible replacement for this that uses WeakRef.
I'd like a few more eyes on it. It's not concurrency-safe, which might
be a good addition. The 'clean' method is also not very efficient, but
Ruby's WeakRef doesn't have the notion of a reference queue you can
poll cheaply. Adding finalizers would be a heavy-weight way to add
support for a reference queue.
Here's the code I've come up with (plus a "require 'weakref'" near the
top of drb/drb.rb):
class DRbIdConv
def initialize
@id2ref = {}
end
# Convert an object reference id to an object.
#
# This implementation looks up the reference id in the local object
# space and returns the object it refers to.
def to_obj(ref)
_get(ref)
end
# Convert an object into a reference id.
#
# This implementation returns the object's __id__ in the local
# object space.
def to_id(obj)
obj.nil? ? nil : _put(obj)
end
def _clean
dead = []
@id2ref.each {|id,weakref| dead << id unless weakref.weakref_alive?}
dead.each {|id| @id2ref.delete(id)}
end
def _put(obj)
_clean
@id2ref[obj.__id__] = WeakRef.new(obj)
obj.__id__
end
def _get(id)
weakref = @id2ref[id]
if weakref
result = weakref.__getobj__ rescue nil
if result
return result
else
@id2ref.delete id
end
end
nil
end
private :_clean, :_put, :_get
end
And here is a test for it:
require 'test/unit'
require 'drb'
require 'java'
class TestWeakDrbIdConv < Test::Unit::TestCase
def test_weak_drb_id_conv
conv = DRb::DRbIdConv.new
obj_ary = []
id_ary = []
# populate
100.times do
obj = Object.new
obj_ary << obj
id_ary << conv.to_id(obj)
end
# confirm they're there
id_ary.each do |id|
assert conv.to_obj(id)
end
# dereference objects and force GC
obj_ary = nil
2.times {java.lang.System.gc}
# confirm they're gone
id_ary.each do |id|
assert !conv.to_obj(id)
end
end
end
- Charlie