This post originated from an RSS feed registered with Ruby Buzz
by Red Handed.
Original Post: Stuffing Your Hand Down the Disposal
Feed Title: RedHanded
Feed URL: http://redhanded.hobix.com/index.xml
Feed Description: sneaking Ruby through the system
Since we have our heads tilted in the direction of ruby+gc, Eustáquio “TaQ” Rangel posted concerns about Ruby’s garbage collector yesterday on Ruby-Talk which led to an interesting bit of code from Yohanes Santoso for watching the garbage collector slurp up variables gone from scope.
Specifically this code which creates a bunch of objects and watches them fade from existence.
class CustObj
attr_accessor :val, :next
def initialize(v,n=nil)
@val = v
@next = n
end
def to_s
"Object #{@val} (#{self.object_id}) points to " \
"#{@next.nil? ? 'nothing' : @next.val} " \
"(#{@next.nil? ? '':@next.object_id})"
end
end
def list
print "Listing all CustObj's with ObjectSpace\n"
print "#{ObjectSpace.each_object(CustObj) {|v| puts v}}" \
" objects found\n\n"
end
begin # start a new scope so we can exit it later
c1 = CustObj.new(1,CustObj.new(2,CustObj.new(3)))
c4 = CustObj.new(4,CustObj.new(5))
c6 = CustObj.new(6)
c1.next.next.next = c1 # comment this and check again
puts "### Initial"
list
c1 = nil
c4.next = nil
GC.start
puts "### After gc, but still within declaring scope"
list
end
puts "### Exited the scope"
list
GC.start # here I want c1 disappears
puts "### After gc, outside of declaring scope"
list
Here’s a great script for understanding how the collector works and how important scope is to the collector. The important variable to watch is Object #1. Notice how, even after its set to nil, its object is still around because it is referenced by Object #3. And it’s still around after the scope is closed. But once the scope is closed and the GC is manually run, Object #1 disappears.
The point of this illustration isn’t to encourage you to run GC manually. It’s to encourage you to use scope to control the variables you’re hanging on to. Even if it means enclosing some stuff in begin..end.
Here’s a little watcher class, based on the above that you can use to monitor the presence of objects:
class GCWatcher
def initialize; @objs = []; end
def watch( obj )
@objs << [obj.object_id, obj.inspect]
obj
end
def list
puts "** objects watched by GcWatcher **"
@objs.each do |obj_id, obj_inspect|
if ( ObjectSpace._id2ref( obj_id ) rescue nil )
puts "#{ obj_inspect } is still around."
else
puts "#{ obj_inspect } is collected."
end
end
puts "** #{ @objs.length } objects watched **"
puts
end
end
Create a GCWatcher object and fill it with references using the watch method. The object will only keep track of object IDs, so it keeps no reference to the actual objects. See a complete sample here. (Inspired by ruby-talk:147351.)