From: Asher Date: 2010-08-25T12:34:15+09:00 Subject: [ruby-core:31843] Garbage Collection Question --Apple-Mail-4--542633760 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=us-ascii This question is no doubt a function of my own lack of understanding, = but I think that asking it will at least help some other folks see = what's going on with the internals during garbage collection.=20 It's best summarized by example code and summary of my understanding of = the resulting output so far as marking an object is concerned. The question in short: when an object goes out of scope and has no = references that were left to it, how does it get collected? Conceptually = this seems easy - the GC walks the heap to see if the contents are still = valid pointers to the heap. Once the pointer is invalid, it makes total = sense to me how the rest proceeds. But how does the pointer to the heap = ever become invalid?=20 Hopefully my example and summary will clarify: require 'pp' class Hash::Weak < Hash =20 def []( key ) # get the stored ID - a FixNum, not an object reference to our = weak-referenced object obj_id =3D super( key.to_sym ) # theoretically this should cause non-referenced objects to get = cleaned up=20 # so long as nothing looks like a pointer or reference to it ObjectSpace.garbage_collect # now get our object from ID # if it had no references it should have been GC'd and we should get = an=20 # rb_eRangeError "is not id value" (expected) or "is recycled = object" (possible) obj =3D ObjectSpace._id2ref( obj_id ) return obj end =20 def []=3D( key, object ) # FixNum have a constant ID for value, so can't be copied and can't = be garbage collected # so object.__id__ cannot be a reference to a child of object and = therefore cannot prevent # garbage collection on the object super( key.to_sym, object.__id__ ) end =20 end ################################################## weak_hash =3D Hash::Weak.new class TestClass end test_object =3D TestClass.new puts 'storing test object' weak_hash[ :key ] =3D test_object puts 'hash now contains object id: ' + weak_hash.pretty_inspect print 'retrieving stored test object from hash (should work/be non-nil): = ' valid_key =3D weak_hash[ :key ] pp valid_key class AnotherClass end puts 'setting variable referring to test object (ID: ' + = test_object.__id__.to_s + ') to nil' test_object =3D nil puts 'ID for variable referring to test object is now: ' + = test_object.__id__.to_s print 'getting test object (should fail with rb_eRangeError): ' invalid_key =3D weak_hash[ :key ] pp invalid_key # error - returns valid key ################################################## # object is created, given an id # variable is assigned to id # variable is changed to new object (including nil) # variable gets the id of new object # previous reference made by variable remains in object space (no valid = references) # gc starts # rb_gc_mark calls gc_mark, marks VM instance = (#) # gc_mark calls gc_mark_children, marks all children of VM # first, the VM class (RubyVM), and then its children # then its class instance (#), and then its children=20 # then its class instance (#>) and its = children # then its class instance (#>) and its = children # then gc_mark_children calls mark_tbl to mark its table (the = class table) # mark_tbl marks all children of the class table, starting = with Class # Class marks its children, first of all # # # marks its children, first of all = #> # #> marks its children, which = includes a table of classes # mark_tbl marks each of classes, which first includes = # # # has a table of entries that it = marks, first of all Object # Object has a table that it marks, first of all = its binding context (presumably main first?) # # # marks its = children, which calls binding_mark, which calls rb_gc_mark, which calls = gc_mark on the Ruby environment: # # # marks its = children which calls env_mark # env_mark calls rb_gc_mark_locations on the = range covered by the environment's declared memory space, which calls = gc_mark_locations # gc_mark_locations calls = mark_locations_array on the space marked by the start and length of = environment # mark_locations_array looks at the = environment as an array of long, and calls is_pointer_to_heap on each = one # if (long)slice is the address of a = valid pointer on the heap, returns TRUE, which causes gc_mark to be = called on the object #***** object, defined by ID, matches = with (long)slice because it has not yet been collected; it is therefore = marked as still existing because it has a valid pointer # =3D> if this were true, no object = would ever be garbage collected; so how is any object ever garbage = collected? Any help understanding what's going on is much appreciated. Thanks, Asher= --Apple-Mail-4--542633760 Content-Transfer-Encoding: quoted-printable Content-Type: text/html; charset=us-ascii This = question is no doubt a function of my own lack of understanding, but I = think that asking it will at least help some other folks see what's = going on with the internals during garbage = collection. 

It's best summarized by example = code and summary of my understanding of the resulting output so far as = marking an object is concerned.

The question in = short: when an object goes out of scope and has no references that were = left to it, how does it get collected? Conceptually this seems easy - = the GC walks the heap to see if the contents are still valid pointers to = the heap. Once the pointer is invalid, it makes total sense to me how = the rest proceeds. But how does the pointer to the heap ever become = invalid? 

Hopefully my example and summary = will clarify:

require = 'pp'

class Hash::Weak = < = Hash
  
  de= f []( key )

   =  # get the stored ID - a FixNum, not an object reference to our = weak-referenced object
    obj_id =3D = super( key.to_sym = )

    # = theoretically this should cause non-referenced objects to get cleaned = up 
    # so long as nothing = looks like a pointer or reference to = it
   =  ObjectSpace.garbage_collect

    # now get our object from = ID
    # if it had no references it = should have been GC'd and we should get = an 
    # rb_eRangeError "is = not id value" (expected) or "is recycled object" = (possible)
    obj =3D = ObjectSpace._id2ref( obj_id = )

   =  return = obj

  end
  
  def []=3D( = key, object = )

    # = FixNum have a constant ID for value, so can't be copied and can't be = garbage collected
    # so = object.__id__ cannot be a reference to a child of object and therefore = cannot prevent
    # garbage = collection on the object
    super( = key.to_sym, object.__id__ = )

  end
  
end
##################################################<= /div>

weak_hash =3D = Hash::Weak.new

class = TestClass
end
test_object =3D = TestClass.new

puts = 'storing test object'
weak_hash[ :key ] =3D = test_object
puts 'hash now contains object id: ' + = weak_hash.pretty_inspect

pr= int 'retrieving stored test object from hash (should work/be non-nil): = '
valid_key =3D weak_hash[ :key = ]
pp = valid_key

class = AnotherClass
end

puts 'setting variable referring to test object (ID: ' + = test_object.__id__.to_s + ') to nil'
test_object =3D = nil
puts 'ID for variable referring to test object = is now: ' + = test_object.__id__.to_s

pri= nt 'getting test object (should fail with rb_eRangeError): = '
invalid_key =3D weak_hash[ :key = ]
pp invalid_key
# error - = returns valid = key

#######################= ###########################

# object is created, given an id
# variable is = assigned to id
# variable is changed to new object = (including nil)
# variable gets the id of new = object
# previous reference made by variable = remains in object space (no valid references)
# gc = starts
# rb_gc_mark calls gc_mark, marks VM = instance (#<RubyVM:0x000001008700b8>)
# = gc_mark calls gc_mark_children, marks all children of = VM
# first, the VM class (RubyVM), and then its = children
#   then its class instance = (#<Class:RubyVM>), and then its = children 
#     then its class = instance (#<Class:#<Class:RubyVM>>) and its = children
#       then its class = instance (#<Class:#<Class:Class>>) and its = children
#         then = gc_mark_children calls mark_tbl to mark its table (the class = table)
#           = mark_tbl marks all children of the class table, starting with = Class
#             = Class marks its children, first of all = #<Class:Module>
#         =       #<Class:Module> marks its children, first of = all #<Class:#<Class:Module>>
#   =               = #<Class:#<Class:Module>> marks its children, which includes = a table of classes
#         =           mark_tbl marks each of classes, which = first includes #<Class:Object>
#   =                   = #<Class:Object> has a table of entries that it marks, first of all = Object
#             =           Object has a table that it marks, = first of all its binding context (presumably main first?) = #<Binding:0x00000100870068>
#     =                     = #<Binding:0x00000100870068> marks its children, which calls = binding_mark, which calls rb_gc_mark, which calls gc_mark on the Ruby = environment: = #<RubyVM::Env:0x00000100854bd8>
#   =                     =     #<RubyVM::Env:0x00000100854bd8> marks its children = which calls env_mark
#         =                     = env_mark calls rb_gc_mark_locations on the range covered by the = environment's declared memory space, which calls = gc_mark_locations
#         =                     =   gc_mark_locations calls mark_locations_array on the space marked = by the start and length of environment
#   =                     =           mark_locations_array looks at the = environment as an array of long, and calls is_pointer_to_heap on each = one
#             =                     =   if (long)slice is the address of a valid pointer on the heap, = returns TRUE, which causes gc_mark to be called on the = object
#*****           =                     =  object, defined by ID, matches with (long)slice because it has not = yet been collected; it is therefore marked as still existing because it = has a valid pointer
# =3D>       =                     =        if this were true, no object would ever be = garbage collected; so how is any object ever garbage = collected?

Any help = understanding what's going on is much = appreciated.

Thanks,
Asher
= = --Apple-Mail-4--542633760--