All posts

Fix Memory Leak in Rails

Fix Ruby on Rails memory leaks from ActiveRecord caching, symbol creation, and improper eager loading in production applications.

Memory Leaks in Rails

Rails applications running under Puma often show steadily growing memory (known as "memory bloat"). Some of this is genuine leaks; some is Ruby's memory allocator behavior.

N+1 Queries Loading Too Many Objects

Loading associated records without eager loading creates many ActiveRecord objects:

# BAD — N+1 and excessive object creation
User.all.each do |user|
  user.posts.each { |post| process(post) }
end

# GOOD — eager load and batch
User.includes(:posts).find_each(batch_size: 500) do |user|
  user.posts.each { |post| process(post) }
end

String-to-Symbol Conversion

Symbols are never garbage collected in Ruby < 2.2. Even in modern Ruby, dynamically creating many unique symbols wastes memory:

# BAD — creates a new symbol for every user input
params.each { |k, v| instance_variable_set(:"@#{k}", v) }

# GOOD — use a hash instead
@attributes = params.to_h

Jemalloc for Memory Fragmentation

Ruby's default allocator (glibc malloc) fragments memory over time. Switch to jemalloc:

RUN apt-get install -y libjemalloc2
ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2
ENV MALLOC_CONF=dirty_decay_ms:1000,narenas:2

Worker Recycling

# config/puma.rb
worker_timeout 60
worker_shutdown_timeout 8

# Use puma_worker_killer gem
before_fork do
  PumaWorkerKiller.enable_rolling_restart(6 * 3600) # Restart workers every 6h
end

Bugsly monitors per-worker RSS in Rails apps and alerts when memory growth trends indicate a leak rather than normal allocation patterns.

Try Bugsly Free

AI-powered error tracking that explains your bugs. Set up in 2 minutes, free forever for small projects.

Get Started Free