If you’re wondering what’s going on with your Ruby application…
There are no fancy GUI tools…
But we have the ObjectSpace module!
ObjectSpace gives you information about the current state of your application.
Let’s discover how it works.
Counting Objects
Using ObjectSpace
you can know what objects are currently ‘alive’ in your program.
What does it mean for an object to be alive?
An object is alive as long as it has any references pointing to it. A reference is just a way to access the object, like a variable or a constant.
If an object can’t be reached then it means that it’s safe to be removed from memory.
Example:
# The variable 'name' holds a reference to the string 'Dave'. name = 'Dave' # The 'name' variable now points to 'John'. # 'Dave' no longer has a reference pointing to it. name = 'John'
Now let’s see an example of ObjectSpace
in action:
require 'objspace' # This is valid Ruby syntax, but doesn't work on irb/pry ObjectSpace .each_object .inject(Hash.new 0) { |h,o| h[o.class] += 1; h } .sort_by { |k,v| -v } .take(10) .each { |klass, count| puts "#{count.to_s.ljust(10)} #{klass}" } # Copy & paste version (use this for irb/pry) ObjectSpace.each_object.inject(Hash.new 0) { |h,o| h[o.class] += 1; h }.sort_by { |k,v| -v }.take(10).each { |klass, count| puts "#{count.to_s.ljust(10)} #{klass}" }
This will print a table with the object count for your top-10 classes.
Count Class ------------------------- 5436 String 315 Class 251 Array 101 Encoding 69 Regexp 45 Hash 26 Module 25 Gem::Version 22 Gem::StubSpecification::StubLine 22 Gem::StubSpecification
If you suspect a memory leak you could log this data every hour & find out if there is some object count that keeps increasing all the time but never goes down.
Fun with Objects
When using ObjectSpace
you get access to the actual objects, not just information about them, so you can do some fun things like printing the value of all the strings or printing the path of all your File
objects.
Example:
ObjectSpace .each_object(String) .sort_by { |s| s.size } .each { |s| p s }
This will print all the in-memory strings, sorted by size. You will notice that there are many strings that you didn’t create yourself, they are created by the Ruby interpreter.
Practical uses?
Well, this is mostly for debugging & gathering stats about your app π
Object Memory Size
Another thing you can do is to use ObjectSpace.memsize_of
to find the memory size of a particular object.
Example:
o = "a" * 100 ObjectSpace.memsize_of(o)
One thing to keep in mind is this warning from the documentation:
“Note that the return size is incomplete. You need to deal with this information as only a HINT.”
If you try this method with different types of objects you will find some interesting things, like Fixnum
s always returning 0.
ObjectSpace.memsize_of(42) # 0
The reason for this is that Ruby doesn’t internally create Fixnum
objects, you can learn more about this in the post I wrote about numbers in Ruby.
Another interesting one are strings:
ObjectSpace.memsize_of("A" * 22) # 40 ObjectSpace.memsize_of("A" * 23) # 40 ObjectSpace.memsize_of("A" * 24) # 65
I use
"A" * size
as a way to create a longer string without having to type it out π
Wait! What did just happen?
Well, it turns out that Ruby has a built-in optimization for strings smaller than 24 characters, that’s why there is a jump in memory use after that. You can see this in more detail in this post from Pat Shaughnessy.
How to Find Aliased Methods
Wouldn’t it be nice if there was a ‘master’ list of all the aliased methods in Ruby?
Wish granted!
Have a look at this:
class Module def aliased_methods instance_methods(false) .group_by { |m| instance_method(m) } .map(&:last) .keep_if { |symbols| symbols.length > 1 } end end
I got this code from a Stackoverflow answer. It defines an aliased_methods
method on the Module
class, which uses the instance_methods
method to get a list of all the instance methods defined on a class.
I know that may sound a bit confusing, but that’s metaprogramming for you!
Here is the rest of the code, which builds an array of all the class names that have at least one ‘alive’ object, then it calls aliased_methods
on every class & prints the output.
objects = ObjectSpace.each_object.map(&:class).uniq objects.each do |klass| methods = "n#{klass}n#{'-'*20}n" klass.send(:aliased_methods).each do |m1, m2| methods << "#{m1.to_s.ljust(15)} #{m2}n" end puts methods end
This is what the output looks like:
Array -------------------- inspect to_s [] slice length size find_index index collect map collect! map!
Conclusion
I hope you enjoyed learning about the cool things you can do with ObjectSpace
, now go try it out and let me know if you find anything interesting!
Don’t forget to share this post with all your programmer friends, it will help them learn something new & it will help me get more readers π
Nice tip… thanks for sharing.
Thanks for reading π
Another good one. Thank you for good stuff!
Thank you for reading π
Awesome Post!
Thank you & thanks for sharing on social networks π
Thanks.
I had fun and found: ObjectSpace.dump_all ;-p lots of things to see for a curious mind π