Created
May 31, 2014 11:56
-
-
Save madis/30f3ef5844806fa42a24 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
#!/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