HeapScope

Ruby object retention, heap growth, and memory leak diagnostics
Not just “how much memory” — why the process is keeping it.

Gem version RubyGems CI MIT Ruby Privacy No SaaS thanks.dev Docs Changelog

RubyGems · Website · Docs hub · Quick start · CLI · Privacy · GitHub Sponsors · thanks.dev · Changelog · Release notes


What HeapScope is

HeapScope is a local, evidence-driven Ruby gem for diagnosing object retention, abnormal heap growth, allocation hot spots, long-lived objects, and leak-shaped patterns in long-running processes.

It is designed for Rails, Puma, Sidekiq, background jobs, CLIs, and CI — distinguishing allocation pressure from retention, and intentional caches from suspicious growth.

No SaaS. No uploads. No telemetry. Everything runs in-process on your machine.

| | | |--|--| | Install (gem) | rubygems.org/gems/heapscope · gem install heapscope | | Site / docs | theworker02.github.io/heapscope | | Source | github.com/theworker02/heapscope | | Changelog | CHANGELOG.md · narrative 0.7.0 | | Release notes | GitHub v0.7.0 | | Sponsor | GitHub Sponsors · thanks.dev |


Why HeapScope

Traditional tools often answer: “How much memory is the process using?”

HeapScope answers: “Why is this Ruby process retaining more memory than expected?”

Every finding separates observed facts, derived behavior, hypothesis, and suspected cause — and never claims "Memory leak confirmed" without strong evidence.


Installation

Install from RubyGems (MRI Ruby ≥ 3.1 recommended):

gem install heapscope

Or add to your Gemfile:

# Gemfile
gem "heapscope", "~> 0.7"
bundle add heapscope

Confirm the install:

heapscope about
heapscope doctor

From source (development / contributing):

git clone https://github.com/theworker02/heapscope.git
cd heapscope
bundle install
bundle exec rake test

Release notes and source tags: GitHub Releases · narrative: docs/changelogs/0.7.0.md


Ruby compatibility

Engine Status
MRI Ruby ≥ 3.1 Primary target — full ObjectSpace / allocation tracing / memsize
JRuby Adapter present; capabilities degrade safely
TruffleRuby Adapter present; capabilities degrade safely
puts HeapScope.capabilities

Quick start

require "heapscope"

report = HeapScope.measure(force_gc: true) { perform_work }
puts report
report.save("report.json")
report.save_html("report.html")

# Ranked follow-ups
puts HeapScope.next_steps(report)

# CI gate
budget = HeapScope.budget_preset(:ci_strict)
HeapScope.check(budget: budget) { perform_work }
heapscope doctor --fix          # write starter heapscope.yml
heapscope snapshot --slim -o before.json
heapscope diff before.json after.json --html report.html --fail-on-medium
heapscope suggest report.json    # next steps + ignore hints
heapscope watch --duration 120 -o watch.json
heapscope about

Full CLI: docs/cli.md


Product surface

Capability How
Snapshots (lightweight / standard / deep / slim JSON) HeapScope.snapshot / heapscope snapshot
Diff & compare HeapScope.compare / heapscope diff
Block measure & multi-cycle retention measure / retention_test
Ranked findings + next steps analyzer + Suggest
Budget presets `Budget.preset(:rails_request\ :sidekiq_job\ :ci_strict)`
Sessions & scorecards HeapScope.session / probe
Watch / monitor with alerts heapscope watch / Monitor
Report packs HeapScope.pack / heapscope pack
Baselines & schema validation baseline / validate
Rails / Rack / Sidekiq / RSpec / Minitest optional require paths

Concepts

Ruby heap vs RSS

HeapScope records both Ruby heap populations and process RSS. RSS growth ≠ Ruby object leak — native extensions, allocators, mmap, and CoW matter.

Allocated vs retained

100,000 allocated + 99,500 freed  → churn
100,000 allocated + 20,000 live   → retention

Forced GC

Opt-in only (force_gc: true). Never enabled by surprise; refuse deep walks in production_safe.


Budgets & CI

HeapScope::Budget.preset(:rails_request)
HeapScope::Budget.preset(:sidekiq_job)
HeapScope::Budget.preset(:ci_strict)

# or hand-tuned
HeapScope::Budget.new(
  max_retained_objects: 1_000,
  max_rss_growth: 30 * 1024 * 1024,
  severity_threshold: :high
)
heapscope baseline create report.json -o baseline.json
heapscope compare baseline.json current.json --threshold 0.5

Guide: docs/guides/ci-budgets.md


Monitoring

monitor = HeapScope::Monitor.start(interval: 10, mode: :lightweight, alert: true)
# ...
report = monitor.stop
heapscope watch --interval 10 --duration 600 -o monitor.json
heapscope flamegraph snapshot.json --format speedscope -o alloc.json

heapscope flamegraph turns captured allocation sites into folded stacks (inferno / flamegraph.pl) or Speedscope JSON. Capture with --track-allocations (or mode: :deep) so sites are present.

Alerts fire on RSS / live-slot spikes between samples (thresholds configurable).


Diagnostics

Code Name
HS001 persistent_class_growth
HS002 high_retention_ratio
HS003 thread_local_retention
HS004 unbounded_collection
HS005 callback_accumulation
HS006 closure_retention
HS007 poor_gc_recovery
HS008 baseline_regression
HS009 high_allocation_pressure
HS010 native_memory_mismatch
heapscope codes
heapscope explain HS001

Encyclopedia: docs/diagnostics/


Configuration

HeapScope.configure do |config|
  config.mode = :standard # lightweight | standard | deep | production_safe | development
  config.track_allocations = false
  config.ignore_patterns << /Zeitwerk/
  config.inspect_values = false # privacy: off by default
end
heapscope doctor --fix --config-out heapscope.yml
heapscope --config heapscope.yml doctor

Branding / funding URLs live in HeapScope::Branding (single source of truth).


Reports

Text, Markdown, versioned JSON, and static HTML — all offline. HTML uses the same brand mark as the site and README. Reports include NEXT STEPS when findings warrant follow-up.

HeapScope.pack(report, "./pack")

Integrations

require "heapscope/middleware"       # Rack sample_rate
require "heapscope/rails"            # request_retention helpers
require "heapscope/sidekiq_middleware"
require "heapscope/minitest"
require "heapscope/rspec"

Core gem requires stdlib only (plus fiddle when available for Windows RSS).


Privacy

By default HeapScope:

  • makes no network calls
  • sends no telemetry / analytics
  • does not serialize object values
  • does not dump ENV, tokens, cookies, or request bodies

See SECURITY.md.


Examples

bundle exec ruby examples/healthy_churn.rb
bundle exec ruby examples/import_leak.rb
bundle exec ruby examples/showcase.rb

See examples/README.md.


Architecture

lib/heapscope.rb                 Public API
lib/heapscope/runtime/*          Engine adapters + RSS
lib/heapscope/collector.rb       Snapshot capture
lib/heapscope/diff.rb            Population diffs
lib/heapscope/analyzer.rb        Findings & suspects
lib/heapscope/findings.rb        Codes + ranking/dedupe
lib/heapscope/suggest.rb         Next steps + ignore hints
lib/heapscope/budget.rb          CI budgets + presets
lib/heapscope/scorecard.rb       Probe + executive scorecard
lib/heapscope/session.rb         Named artifact sessions
lib/heapscope/report/*           Text / HTML / Markdown
lib/heapscope/cli/               Modular CLI commands

Roadmap

See docs/ROADMAP.md. Current focus: deeper precision and optional CI marketplace packaging — not parallel product surfaces.


Contributing

See CONTRIBUTING.md and CODE_OF_CONDUCT.md.

bundle install
bundle exec rake test
bundle exec rubocop
gem build heapscope.gemspec

License

MIT © @theworker02 — see LICENSE.

Sponsor: GitHub Sponsors · thanks.dev/u/gh/theworker02



When top says memory is growing but profiling won’t say why — reach for HeapScope.