Memory leaks are my least favourite type of bug. To track them down requires not only a detailed knowledge of the entire codebase but also strong intuition (or a lot of luck). To make the process more fun I’ve written a patch for ruby 1.9.3 that lets you visualize portions of the memory graph.
ObjectSpace.each_object
Ruby already comes with ObjectSpace
which contains a few methods for analyzing your program. The most useful for finding memory leaks is ObjectSpace.each_object
which yields every single ruby object in your program.
By dumping the counts into a file after each request and using diff
it’s possible to determine what kind of objects are leaking. This is essential to know, but it doesn’t give you any insight into why they’re not being garbage collected.
ObjectSpace.find_references
The first step in getting more insight is to find everything that references the object that is being leaked. Ruby already knows how to calculate this information, as it’s needed for the mark and sweep garbage collection, but it doesn’t expose it to ruby code. The patches add a find_references
method to ObjectSpace
that returns an array of all objects that reference the object you’re looking for.
This works but it turns out to be far too fiddly for manual use while debugging. The main problem is that every time you run find_references
the array that is returned adds a whole slew of additional bogus references. This pollutes the reference graph quickly, so you continually have to restart the process to flush these out.
ObjectGraph#view!
The next step is to recursively run find_references
until you’ve generated a graph that shows all the ways in which your object is referenced. This is wrapped up by the ObjectGraph
class, which takes care to avoid the bogus-references problem. The most useful thing this class can do is let you view!
the graph using graphviz.
While 9 is a silly example it hopefully gives you a feel for how easy it is to reason about the resulting graph. You’ll notice that not only is each node labelled with the inspect output for the object, each edge is also labelled in a way that corresponds to how the reference was made. If you need programmatic access to this information then the annotated_edges
method returns an array of edges along with their description.
Installation
My patches add both ObjectSpace.find_references
and a new ObjectGraph
standard library. If you want to use them you can either clone my repository and build the find-references
branch yourself, or use one of the quick methods below:
Using rvm with a patch:
Using rbenv with ruby-build:
Using chruby with ruby-build:
To get pretty pictures, you’ll also need to brew install graphviz
or apt-get install graphviz
.