Preface:
It's useful to know the memory characteristics of different Ruby versions.
2.0 Uses less memory, but is slower - GC blocks while running
2.1 Generational GC - more memory, but faster GC meaning faster apps (split: old / young heap spaces)
2.2 Introduces incremental GC - marks in steps, reducing stop-the-world time, making OOB GC redundant.
See here for some more reading -
2.1 http://tmm1.net/ruby21-rgengc/
2.2 http://engineering.heroku.com/blogs/2015-02-04-incremental-gc
We could run one extra instance of a passenger worker on a 1gb Heroku dyno in 2.0, but needed it for the same throughput.
-
If you're running multiple processes (Unicorn, Passenger), you can see the memory usage per process. With shell access this is easy, otherwise (on Passenger) - https://status-service.phusionpassenger.com/
-
Know what your cold-memory size is, and which gems are sizeable - https://github.com/schneems/derailed_benchmarks - this needs Ruby 2.1 or higher
bundle exec derailed bundle:mem
-
Log params in production, and ensure you can tie it back to a process, which will isolate anything with leaks or large allocations. Also useful - https://devcenter.heroku.com/articles/log-runtime-metrics
-
If you have a leak, the tooling is getting better, but re-writing it in Java is probably quicker.