Skip to content

Instantly share code, notes, and snippets.

@madis
Created May 31, 2014 11:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save madis/30f3ef5844806fa42a24 to your computer and use it in GitHub Desktop.
Save madis/30f3ef5844806fa42a24 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
require 'ruby-mass' # gem install ruby-mass
NUMBER_OF_RUNS = 30
# No OOP in here :) Just some small snippets for measuring show Ruby's GC
# collects finished Thread objects. A question that popped up at Fortumo
# party.
def report_object_count
count = 0
thread_count = 0
ObjectSpace.each_object {|x| count += 1}
ObjectSpace.each_object(Thread) {|x| thread_count += 1}
"Total object count: #{count} (Thread objects count: #{thread_count})"
end
def live_thread_objects_count
Mass.count(Thread)['Thread']
end
DELAYED_RESULTS = []
def measure_for_reporting_with_delay(delay_seconds, &block)
DELAYED_RESULTS << Thread.new {
sleep delay_seconds
block.call
}
end
def print_delayed_results
DELAYED_RESULTS.each {|t| puts t.value }
end
# Will be ~15 (or NUMBER_OF_RUNS / 2) Because half of the threads have been spawned (and they are spawned all together)
def measure_live_thread_count_in_the_middle(message)
measure_for_reporting_with_delay(NUMBER_OF_RUNS / 2 * 0.01) {
"#{message}: The number of threads somewhere in the middle of first spawning: #{live_thread_objects_count}"
}
end
GC.start # Do some cleanup before start
# Report numbers before starting
puts report_object_count
# Create 100 threads, each sleeping a bit longer than last so
# they finish in sequence. This is to show that as they finish
# they will be garbage collected one by one.
#
# E.g.
# 1. sleep 0.01 seconds
# 2. sleep 0.02 seconds
# ...
# 100. sleep 1 second
#
NUMBER_OF_RUNS.times { |t| Thread.new { sleep t * 0.01 } }
measure_live_thread_count_in_the_middle('Step I')
NUMBER_OF_RUNS.times { |t| puts "#{t} #{report_object_count}" ; sleep t * 0.01 }
# Step II
# Run garbage collection manually between
# Do cleanup before next test
GC.start
puts "And now with forcing garbage collection between counting"
# Notice that the object count doesn't increase
NUMBER_OF_RUNS.times { |t| Thread.new { sleep t * 0.01 } }
measure_live_thread_count_in_the_middle('Step II')
NUMBER_OF_RUNS.times { |t| puts "#{t} #{report_object_count}" ; GC.start; sleep t * 0.01 }
# The next line reports 2 because one thread object is in the DELAYED_RESULTS array
# and another is the main thread Thread.current
puts "Thread object count in memory after both spawnings have finished #{live_thread_objects_count}"
puts "Results of async measurements:"
print_delayed_results
# Conclusion:
# Ruby collects the finished thread objects during garbage collection
# (triggered automatically or manually by you).
#
# It can also collects if it wishes so at its own whim as can be seen from
# the Step I results Where Thread count decreases to 16 somwhere around
# middle but not to 3 as happens in Step II where I force garbage
# collection.
#
# Happy hacking :)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment