Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Example seeming to demonstrate that unary ampersand operator holds on to obj references in Ruby 2.1.x
# Create some special classes to facilitate tracking allocated objects.
class TrackedArray < Array; end
class TrackedString < String; end
STRANG = "a" * 5000
class ClingyObjects
def generate(should_cling = false)
strs = TrackedArray.new
30000.times { strs << TrackedString.new(STRANG) }
char_count = 0
# I'm not sure why, but using the unary & operator on the Array, whether
# through #each or #map, prevents the allocated objects from being GC'd.
# Maybe I'm missing something, but after this method returns nothing
# should refer to the strs Array or any of the objects contained in the
# Array, so GC should proceed without issue. What gives?
strs.each(&:length) if should_cling
strs.each {|x| char_count += x.length }
char_count
end
# Helper to print object allocation stats.
def object_stats(tag)
puts "#{tag}:"
puts "TrackedArray: #{ObjectSpace.each_object(TrackedArray).count}"
puts "TrackedString: #{ObjectSpace.each_object(TrackedString).count}"
end
def print_with_stats(char_count)
object_stats("Before GC")
# Run the garbage collector.
GC.start
object_stats("After GC")
puts char_count
end
end
def wrapper
clinger = ClingyObjects.new
puts "Non-clingy:"
count = clinger.generate
clinger.print_with_stats(count)
puts "\nClingy:"
count = clinger.generate(:should_cling)
clinger.print_with_stats(count)
# Try to GC again for fun
puts "\nTry GC again"
GC.start
clinger.print_with_stats(count)
puts "\nDitch clinger and try GC again"
clinger = nil
5.times do
GC.start
puts "TrackedArray: #{ObjectSpace.each_object(TrackedArray).count}"
puts "TrackedString: #{ObjectSpace.each_object(TrackedString).count}"
puts "\nSleep a bit and try again"
sleep 3
end
puts "TrackedArray: #{ObjectSpace.each_object(TrackedArray).count}"
puts "TrackedString: #{ObjectSpace.each_object(TrackedString).count}"
end
wrapper
puts "TrackedArray: #{ObjectSpace.each_object(TrackedArray).count}"
puts "TrackedString: #{ObjectSpace.each_object(TrackedString).count}"
# Non-clingy:
# Before GC:
# TrackedArray: 1
# TrackedString: 30000
# After GC:
# TrackedArray: 0
# TrackedString: 0
# 150000000
# Clingy:
# Before GC:
# TrackedArray: 1
# TrackedString: 30000
# After GC:
# TrackedArray: 1
# TrackedString: 30000
# 150000000
# Try GC again
# Before GC:
# TrackedArray: 1
# TrackedString: 30000
# After GC:
# TrackedArray: 1
# TrackedString: 30000
# 150000000
# Ditch clinger and try GC again
# TrackedArray: 1
# TrackedString: 30000
# Sleep a bit and try again
# TrackedArray: 1
# TrackedString: 30000
# Sleep a bit and try again
# TrackedArray: 1
# TrackedString: 30000
# Sleep a bit and try again
# TrackedArray: 1
# TrackedString: 30000
# Sleep a bit and try again
# TrackedArray: 1
# TrackedString: 30000
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.