Skip to content

Instantly share code, notes, and snippets.

@jjb
Last active December 2, 2024 09:16
Show Gist options
  • Save jjb/9ff0d3f622c8bbe904fe7a82e35152fc to your computer and use it in GitHub Desktop.
Save jjb/9ff0d3f622c8bbe904fe7a82e35152fc to your computer and use it in GitHub Desktop.
Using Jemalloc 5 with Ruby.md

For years, people have been using jemalloc with ruby. There were various benchmarks and discussions. Legend had it that Jemalloc 5 didn't work as well as Jemalloc 3.

Then, one day, hope appeared on the horizon. @wjordan offered a config for Jemalloc 5.

Ubuntu/Debian

FROM ruby:3.1.2-bullseye
RUN apt-get update ; \
    apt-get install -y --no-install-recommends libjemalloc2 ;
ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2
ENV MALLOC_CONF='dirty_decay_ms:1000,narenas:2,background_thread:true'

Alpine

FROM ruby:3.2.3-alpine3.19
RUN apk add --update jemalloc
ENV LD_PRELOAD=/usr/lib/libjemalloc.so.2
ENV MALLOC_CONF='dirty_decay_ms:1000,narenas:2,background_thread:true'

Heroku

  1. add the apt buildpack (maintained by Heroku staff and owned by the Heroku namespace, but still considered "third-party"/"unofficial")
    heroku buildpacks:add --index 1 heroku-community/apt
  2. in the same branch, create and deploy these two files:
    • Aptfile
      libjemalloc2
      
    • .profile
      export LD_PRELOAD=libjemalloc.so.2
      export MALLOC_CONF=dirty_decay_ms:1000,narenas:2,background_thread:true
  • ENV vars are set in .profile instead of config:set so that jemalloc is only used at runtime and not compile time, and to avoid (harmless) noise when first starting a shell. see discussion here
  • There is also the third-party heroku-buildpack-jemalloc, which will download, build, and install an arbitrary jemalloc version, which is useful if you need a version not distributed by Ubuntu for some reason, such as before the discovery of the improved MALLOC_CONF, necessitating the installation of version 3.6

MALLOC_CONF options

  • dirty_decay_ms:1000,narenas:2,background_thread:true comes from dockerfile-rails. code discussion
  • This is the original "tradeoff between memory and performance". it is likely identical to the above config on recent Jemalloc and Ruby versions, because the extra values are the defaults dirty_decay_ms:1000,muzzy_decay_ms:0,narenas:2,background_thread:true,thp:never
  • This is "more heavily memory-optimized, like jemalloc 3.6" (the version used by Fullstaq Ruby) dirty_decay_ms:0,muzzy_decay_ms:0,narenas:2,background_thread:true,thp:never
  • This is used by gitlab 1 2 narenas:2

Check if jemalloc is working:

MALLOC_CONF="stats_print:true" ruby -e "exit" # will produce no output if jemalloc not being used, a wall of text if it is
MALLOC_CONF="$MALLOC_CONF,stats_print:true" ruby -e "exit" # if a config is set, you can include it like this

Links!

@jjb
Copy link
Author

jjb commented Aug 10, 2023

Info from Shopify employee in a slack group (shared with permission):

We use version 5 without any tuning, mostly because for us latency (generally) has priority over memory usage

@technicalpickles
Copy link

At Gusto, we're using jemalloc 3 via fullstaq ruby

@jjb
Copy link
Author

jjb commented Aug 11, 2023

Info from a GitHub employee in a slack group (shared with permission):

jemalloc3: no MALLOC_CONF afaict

@jjb
Copy link
Author

jjb commented Dec 12, 2023

Apparently the ruby binary can be patched, so that neither compiling ruby with jemalloc nor loading it with LD_PRELOAD is necessary:

apt install patchelf
patchelf --add-needed libjemalloc.so.2 /usr/local/bin/ruby
apt-get purge -y patchelf

taken from here, more info here

@rgaufman
Copy link

rgaufman commented Feb 6, 2024

We've been using jemalloc since 2018 and we literally went from needing to restart our Ruby app every 3-4 days as the memory leaks to memory never leaking, ever and taking a third of without jemalloc. I do not understand why this is not the default for all Ruby builds!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment