Memory and Garbage Collection
Prerequisites: Frozen objects and mutability
Ruby manages memory for you: you create objects, and the garbage collector (GC) frees the ones nothing refers to any more. You never free memory by hand. But in long-running programs — web servers, background workers — how many objects you create affects both speed and memory use.
How Ruby's GC works, briefly
- Objects live on Ruby's heap. When it needs space, the GC marks every object still reachable from your program (variables, constants, the objects they refer to...) and sweeps away the rest.
- The GC is generational: most objects die young, so new objects are checked often and long-lived ones rarely.
- It's incremental and compacting, which keeps pauses short and memory less fragmented.
Every allocation is work the GC must later do. So the practical skill is allocating less in hot code.
Measuring allocations
def allocations
before = GC.stat(:total_allocated_objects)
yield
GC.stat(:total_allocated_objects) - before
end
words = %w[alpha beta gamma] * 1000
a = allocations { words.map { |w| w.upcase }.select { |w| w.start_with?("A") } }
b = allocations { words.each_with_object([]) { |w, out| out << w.upcase if w.start_with?("a") } }
puts a > b
true
The first version upper-cases every word and builds a full intermediate array; the second only allocates strings for the words it keeps.
Habits that reduce allocations
# frozen_string_literal: true— string literals are shared instead of copied each time the line runs.- Symbols for internal names and hash keys.
- Bang methods (
map!,strip!,<<) on objects you own, instead of building a new object on each step. - Streaming (
File.foreach,each_slice, database batches) instead of loading everything into memory at once.
Memory leaks in Ruby
Garbage collection prevents classic leaks, but memory still grows if objects stay reachable: a constant or class-level hash used as a cache that never evicts entries, or an ever-growing array of log lines. When a process's memory climbs steadily, look for collections that only ever grow. Tools such as the memory_profiler gem report where objects are allocated and retained.
Common mistakes
- Optimizing allocations in code that runs once. Measure first — only hot paths are worth it.
- Calling
GC.startin application code to "fix" memory. It hides the symptom and costs time; find what's keeping objects alive instead.