Lower Memory Usage of your Rails App with Jemalloc.🍭


Some of you might know that we are bootstrapped with a shoestring budget.

This means that we are frugal about how we budget for resources and buy-into only more carefully. Which is great because then our end users get to enjoy performance of a well tuned app top down.

Through this post we explain how we used jemalloc—a memory allocator—to improve performance of our Rails application. It helped us reduce our RAM usage by 30%.

But 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. Switching over to jemalloc has been discussed often by experts in the Ruby/Rails community but a decision is yet to come. We are hoping this thought will see the light of the day soon and jemalloc will be considered by the Ruby core group.

It is worthwhile to mention here that several large projects and organizations like Redis, GitHub, GitLab and Discourse are already using jemalloc on production.

Jemalloc

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

While it will take some time for ruby aficionados to arrive on this decision, we decided to take full 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.

An improvement of ~28.62% (>1 Gb RAM) on memory usage was registered on our production server (Linode), but YMMV.

Here are our before and after 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

A single instance of Linode with ~8Gb RAM is at our disposal.

How to install:

To install jemalloc on your linux server (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, you can use homebrew to install jemalloc, like so:

$ brew install jemalloc

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


$ RUBY_CONFIGURE_OPTS=--with-jemalloc rbenv install 2.4.1 	# As of September, 2018

If ruby is already installed on your machine as it was in our case you might need to purge and remove older shims and rehash it again, like so:


$ 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 if your installation of Ruby is now using jemalloc, try following command:

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

Presence of a string like -ljemalloc in the output indicates that the memory allocator library we wanted is correctly loaded before starting ruby. Brilliant! Now let’s go ahead and restart our app, and there it is folks: a faster rails app that’s also light on your wallet.

Like it? Great! Do you have more tips or tricks to suggest for vertical scaling?


Hi! I’m Marvin Danig CEO and Cofounder of Bubblin Superbooks.

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