ObjectSpace: Ruby's Hidden API
Ruby’s ObjectSpace module provides direct access to the runtime’s object management system. It’s a powerful introspection tool that most developers never use, but understanding it reveals how Ruby manages memory and objects under the hood.
What is ObjectSpace?
ObjectSpace is a standard library module that lets you enumerate and interact with all living objects in your Ruby process. It’s essentially a window into the garbage collector’s world.
require 'objspace'
# Count all objects by class
counts = Hash.new(0)
ObjectSpace.each_object { |obj| counts[obj.class] += 1 }
puts counts.sort_by { |_, v| -v }.first(5)
Dangerous Powers
Finding All Instances
You can find every instance of a specific class currently in memory:
class User
attr_reader :name
def initialize(name) = @name = name
end
user1 = User.new("Alice")
user2 = User.new("Bob")
ObjectSpace.each_object(User) { |u| puts u.name }
# Outputs: Bob, Alice
This capability is both powerful and dangerous. It breaks encapsulation completely—you can access objects that weren’t meant to be discoverable.
Memory Forensics
ObjectSpace shines for debugging memory issues:
require 'objspace'
# Get detailed memory information about an object
obj = "hello" * 1000
puts ObjectSpace.memsize_of(obj) # bytes used
# Find where objects are allocated
ObjectSpace.trace_object_allocations_start
array = Array.new(100) { "test" }
ObjectSpace.trace_object_allocations_stop
array.each do |str|
file = ObjectSpace.allocation_sourcefile(str)
line = ObjectSpace.allocation_sourceline(str)
puts "Allocated at #{file}:#{line}"
end
Object IDs and Weak References
Every Ruby object has a unique object_id. ObjectSpace lets you retrieve objects by their ID:
str = "original"
id = str.object_id
# Later, retrieve the object if it still exists
obj = ObjectSpace._id2ref(id)
puts obj # "original"
Be careful: if the object has been garbage collected, _id2ref will raise an error or return unexpected results.
Weak References
Ruby 3.1 added ObjectSpace::WeakMap for storing references that don’t prevent garbage collection:
map = ObjectSpace::WeakMap.new
obj = Object.new
map[obj] = "some value"
# obj can still be GC'd despite being in the map
# map[obj] will return nil after GC
Heap Dumps
For serious memory debugging, you can dump the entire heap:
require 'objspace'
ObjectSpace.trace_object_allocations_start
# ... run your code ...
File.open('heap.json', 'w') do |f|
ObjectSpace.dump_all(output: f)
end
This creates a JSON file with every object in memory, which can be analyzed with tools like heap-analyzer or imported into profiling tools.
Performance Implications
ObjectSpace operations are slow. each_object must pause the world and enumerate potentially millions of objects. Never use ObjectSpace in production code paths—reserve it for debugging, development tools, and test introspection.
When to Use ObjectSpace
ObjectSpace is invaluable for:
- Memory leak debugging: Finding unexpected object retention
- Development tools: Building REPLs, debuggers, or inspection tools
- Testing: Verifying objects are properly cleaned up
- Metaprogramming: Advanced runtime manipulation (use sparingly)
The Philosophy
ObjectSpace exemplifies Ruby’s philosophy: give developers powerful tools, even if they’re dangerous. It assumes you’re a responsible adult who understands the tradeoffs. With great power comes great responsibility—and great memory dumps.