Counting live objects with ObjectSpace
What you already know, in two sentences
The lesson "Memory management", in Operating system basics, covered the heap-of-slots model, mark and sweep, generational GC and GC.stat's heap_allocated_pages/heap_live_slots/count fields. It also covered find_each and frozen string literals as memory patterns in Rails. None of that is repeated here. This course assumes you know Ruby has a garbage collector and roughly how it decides what's garbage. All seven lessons go where that lesson didn't: watching the GC work on your own objects, and steering it.
How many of these exist right now?
class Widget; end
ObjectSpace.each_object(Widget).count
# 0
10.times { Widget.new }
ObjectSpace.each_object(Widget).count
# 10
ObjectSpace.each_object(SomeClass) walks every live object of that class on the heap, right now, in this process. It returns an enumerator you can .count, .to_a or iterate. Before any Widget exists, the count is 0; after creating ten, it's 10. This is a different kind of tool from GC.stat. GC.stat gives aggregate numbers such as total slots and pages, while this gives you the live objects of one class, which you can inspect one by one.
Why this matters more than it sounds
"How many Catalog::Course objects exist right now?" sounds like a toy question. It's the question behind a real memory leak, where some object graph keeps growing because something holds a reference nobody meant to keep. ObjectSpace.each_object answers it without guessing, by asking the heap itself instead of reasoning about the code from outside.
ObjectSpace.each_object(String).count
# a real number, likely in the thousands, even in a fresh process
In a plain Ruby session, thousands of String objects already exist before your code allocates anything; Ruby's standard library and the interpreter's boot create them. Keep that in mind before drawing conclusions from a raw object count. A large number on its own doesn't mean a leak. It gives you a baseline to compare against after your code runs.
Iterating as well as counting
each_object also hands you the live objects:
ObjectSpace.each_object(Widget) { |w| puts w.object_id }
This runs the block once per live Widget in this process, with the object_id of each. You'd rarely leave it running in production, because walking the whole heap for a class has a cost. As a one-off diagnostic in a console session investigating "why does memory keep climbing?", it's the most direct answer Ruby gives.
Key takeaways
ObjectSpace.each_object(SomeClass).countcounts every live instance of a class on the heap right now, going from 0 to 10 after creating tenWidgets.- It's a different tool from
GC.stat: the live objects of one class, not aggregate heap numbers. - A raw object count needs a baseline; thousands of
Stringobjects exist in a fresh Ruby process before your code runs anything. each_objectalso iterates the objects themselves, useful for one-off diagnostics, not for routine use in production.
Keep going with lesson 2
What ObjectSpace.count_objects reveals
Reading the heap by internal type with ObjectSpace.count_objects and comparing snapshots before and after suspect code.
A free account also gives you the quizzes, your progress and notes, and the discussion.