Created
August 25, 2010 03:08
-
-
Save Asher-/548773 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'pp' | |
class Hash::Weak < Hash | |
def []( key ) | |
# get the stored ID - a FixNum, not an object reference to our weak-referenced object | |
obj_id = 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 = ObjectSpace._id2ref( obj_id ) | |
return obj | |
end | |
def []=( 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 | |
################################################## | |
weak_hash = Hash::Weak.new | |
class TestClass | |
end | |
test_object = TestClass.new | |
puts 'storing test object' | |
weak_hash[ :key ] = 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 = 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 = 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 = 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 | |
# => if this were true, no object would ever be garbage collected; so how is any object ever garbage collected? |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment