Memstat: Fast memory statistics & Better Out-of-band GC
Ruby 2.1 introduced generational garbage collection. It is a major improvement in terms of shorter GC pauses and overall higher throughput, but that comes with a drawback of potential memory bloat.
You can mitigate the bloat by manually running GC.start, but like Unicorn's out-of-band GC, doing it after every request can seriously hurt the performance. You want to run GC.start only when the process gets larger than X MB.
memstat offers a fast way to retrieve the memory usage of the current process, by providing direct access to /proc/[pid]/status and /proc/[pid]/smaps.
If you've ever called the ps -o rss command from inside a Ruby process to capture real memory usage, chances are, you've already learned that it is very slow.
That's because shelling out ps creates an entire copy of the ruby process - typically 70-150MB for a Rails app - then wipe out those memory with the executable of ps. Even with copy-on-write and POSIX-spawn optimization, you can't beat the speed of directly reading statistics from memory that is maintained by the kernel.
With a minimal Ruby program, memstat is 10 times faster than ps -o rss. The speed diff only gets wider when your Ruby process is bigger.
user system total real
ps: 0.010000 0.040000 0.160000 ( 0.168328)
memstat: 0.020000 0.000000 0.020000 ( 0.017127)
Installation
Add this line to your application's Gemfile:
gem 'memstat'
Or install it yourself as:
$ gem install memstat
Usage
if Memstat.linux?
status = Memstat::Proc::Status.new(pid: Process.pid)
if status.rss > 150.megabytes
GC.start
end
end
For Unicorn, add these lines to your config.ru (should be added above loading environment) to check memory size on every request and run GC out-of-band:
require 'memstat'
use Memstat::OobGC::Unicorn, 150*(1024**2) # Invoke GC if the process is bigger than 150MB
Contributing
- Fork it
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create new Pull Request