Lower memory usage of your Rails app with jemalloc.🍭

Marvin Danig

Some of you may know that we are a bootstrapped startup on a shoestring budget. This means we are frugal and use resources like RAM only more carefully. Which is great because the end users get to enjoy performance of a well tuned app. In this post we explain how we used jemalloc—a memory allocator—to improve performance of our Rails application.

It helped us lower RAM usage by 30%. First, what is jemalloc?

jemalloc is a general purpose malloc(3) implementation that emphasizes fragmentation avoidance and scalable concurrency support.

By default Ruby uses the glibc for memory allocation. Using jemalloc instead of glibc was discussed recently by experts in the Ruby community. Hopefully it will see light of the day soon by the ruby-lang core group. It’s notable to mention here that several large projects and organizations like Redis, GitHub, GitLab and Discourse are using jemalloc on production.

While ruby aficionados ruminate on their next move, we decided to take advantage of what is available immediately. Turns out, by compiling Ruby with jemalloc we were able to reduce memory usage of our Rails application quite drastically. We registered an improvement of 28.62% (>1 Gb RAM) on our overall memory usage on Linode, but YMMV, of course.

Here are our actual stats:

# ssh into your server  # before jemalloc:
$ free m total used free shared buff/cache available
Mem: 8162148 4126424 2439396 282816 1596328 3604716
Swap: 524284 90064 434220 # after jemalloc 
$ free m total used free shared buff/cache available
Mem: 8162148 2945404 3157684 282840 2059060 4785128
Swap: 524284 85200 439084 

FYI, we’re on just one Linode instance with ~8Gb RAM at our disposal.


To install jemalloc on Ubuntu 16.04.1 LTS:

# ssh into your server or on your dev machine.  $ sudo apt-get update
$ sudo apt-get install libjemalloc-dev # Install jemalloc 

On macOS, use Homebrew to install jemalloc:

Once jemalloc is set up use rbenv or RVM to (re)install ruby 2.4.1 or whichever version of Ruby your app needs with a prefix like so:

$ RUBY_CONFIGURE_OPTS=--with-jemalloc rbenv install 2.4.1 # Bubblin uses Ruby 2.4.1 

If Ruby is already installed on your machine, as it was in our case, you may need to delete the older shims and rehash Ruby again:

$ RUBY_CONFIGURE_OPTS=--with-jemalloc rbenv install 2.4.1 ➜ rbenv: /home/marvin/.rbenv/versions/2.4.1 already exists continue with installation? (y/N) y Downloading ruby-2.4.1.tar.bz2... -> https://cache.ruby-lang.org/pub/ruby/2.4/ruby-2.4.1.tar.bz2 Installing ruby-2.4.1... ruby-build: use readline from homebrew Installed ruby-2.4.1 to /home/marvin/.rbenv/versions/2.4.1 rbenv: cannot rehash: /home/marvin/.rbenv/shims/.rbenv-shim exists $ rm /home/marvin/.rbenv/shims/.rbenv-shim
$ rbenv rehash

To check that your installation of Ruby is using jemalloc, run the following command:

$ ruby -r rbconfig -e "puts RbConfig::CONFIG['LIBS']" -lpthread -ljemalloc -lgmp -ldl -lcrypt -lm # Output

Presence of -ljemalloc in the output indicates that the memory allocator library we wanted is correctly loaded before starting Ruby. Now go ahead and restart your app, and there it is folks: a faster Rails app that’s light on your wallet.

Like it? Great! Do you have more tips or tricks to suggest for vertical scaling? Please share on comments below!

P.S.: Did you know that we bring books straight on web with Bubblin Superbooks?