Ruby 3.4.4 with YJIT and jemalloc: A Performance Deep Dive

August 01, 2025 • By Vladimir Elchinov

Ruby performance has come a long way since the early days of Rails. With Ruby 3.4.4, we now have access to production-ready JIT compilation and advanced memory management that can significantly boost your Rails application's performance. At Rails Blueprint, we've implemented these optimizations from day one, and the results speak for themselves.

The Performance Problem

Standard Ruby installations, while developer-friendly, leave performance gains on the table. Most Rails applications run with:
  • No JIT compilation - Missing 15-30% performance improvements
  • Default memory allocator - Higher memory usage and fragmentation
  • Suboptimal garbage collection - More frequent GC pauses
Rails Blueprint addresses all of these issues with a carefully configured Ruby setup.

YJIT: The Game-Changing JIT Compiler

What is YJIT?

YJIT (Yet Another Ruby JIT) is Ruby's production-ready Just-In-Time compiler, introduced in Ruby 3.1 and significantly improved in 3.4. Unlike previous JIT implementations, YJIT:
  • Compiles hot code paths to native machine code
  • Optimizes incrementally - only the code that needs it
  • Maintains Ruby semantics - no behavioral changes
  • Delivers consistent gains - 15-30% performance improvement

Real-World Impact

In Rails Blueprint applications, YJIT shines in:
Command Objects: Our heavy use of command objects in app/commands/ with dry-validation benefits significantly from JIT compilation of repeated business logic.
View Components: Component-based views with the view_component gem see faster rendering through optimized template compilation.
Background Jobs: Good Job processing gets a boost from JIT-compiled job execution paths.

Enabling YJIT in Production

Rails Blueprint automatically enables YJIT in production through environment configuration:
# config/environments/production.rb
# YJIT is enabled via RUBY_YJIT_ENABLE=1 environment variable
# This is set in our deployment scripts and Docker configuration
lang-ruby
You can verify YJIT is running:
ruby --yjit -e "puts RubyVM::YJIT.enabled?"
# => true
lang-bash

jemalloc: Superior Memory Management

Why jemalloc?

Ruby's default memory allocator (typically glibc malloc) is general-purpose but not optimized for Ruby's allocation patterns. jemalloc provides:
  • 10-20% memory usage reduction
  • Better cache locality for improved performance
  • Reduced memory fragmentation
  • More predictable allocation patterns

Memory Allocation Patterns in Rails

Rails applications have specific memory patterns that jemalloc handles better:
Small object allocation: Ruby creates many small objects (strings, arrays, hashes) that jemalloc manages more efficiently.
Garbage collection: jemalloc's design reduces GC pressure by minimizing fragmentation.
Request handling: Per-request memory allocation and deallocation patterns benefit from jemalloc's optimizations.

Installation Guide

macOS Installation

# Install jemalloc
brew install jemalloc

# Install rust (required for YJIT)
brew install rust

# Install Ruby with optimizations
RUBY_CONFIGURE_OPTS="--enable-yjit --with-jemalloc=$(brew --prefix jemalloc)" rbenv install 3.4.4
lang-bash

Ubuntu/Debian Installation

# Install dependencies
sudo apt-get update
sudo apt-get install -y libjemalloc-dev rustc

# Install Ruby with optimizations
RUBY_CONFIGURE_OPTS="--enable-yjit --with-jemalloc" rbenv install 3.4.4
lang-bash

Verification

Check Ruby Version:
ruby --version
# Expected: ruby 3.4.4 (2025-05-14 revision a38531fd3f) +PRISM [x86_64-...]
lang-bash
Verify YJIT is Available:
ruby --yjit -e "puts RubyVM::YJIT.enabled?"
# Expected: true
lang-bash
Verify jemalloc is Linked (Linux):
ldd $(rbenv which ruby) | grep jemalloc
# Expected: libjemalloc.so.2 => /lib/.../libjemalloc.so.2
lang-bash
Verify jemalloc is Linked (macOS):
otool -L $(rbenv which ruby) | grep jemalloc
# Expected: jemalloc library path
lang-bash

Production Configuration

Docker Setup

Rails Blueprint includes optimized Docker configuration:
# Install jemalloc in production container
RUN apt-get install --no-install-recommends -y curl libjemalloc2 libsqlite3-0 libvips

# Entrypoint script automatically enables jemalloc
#!/bin/bash -e
# Enable jemalloc for reduced memory usage and latency.
if [ -z "${LD_PRELOAD+x}" ] && [ -f /usr/lib/*/libjemalloc.so.2 ]; then
  export LD_PRELOAD="$(echo /usr/lib/*/libjemalloc.so.2)"
fi
lang-dockerfile

Environment Variables

# Enable YJIT
RUBY_YJIT_ENABLE=1

# jemalloc is loaded via LD_PRELOAD
# This is handled automatically by the entrypoint script
lang-bash

Performance Benchmarking

Measuring the Impact

Here's how to benchmark your Rails Blueprint application:
# Create a benchmark script: script/performance_test.rb
require 'benchmark'

def simulate_request_cycle
  # Simulate typical Rails Blueprint request:
  # 1. Authentication check
  # 2. Command object execution
  # 3. View component rendering
  # 4. Background job enqueue

  user = User.find(1)
  result = SomeCommand.call(user: user, params: sample_params)
  MyComponent.new(data: result.data).render_in(view_context)
  SomeJob.perform_later(result.id)
end

# Benchmark with and without YJIT
Benchmark.bm do |x|
  x.report("without YJIT") { 1000.times { simulate_request_cycle } }
  # Enable YJIT and run again
  RubyVM::YJIT.enable
  x.report("with YJIT") { 1000.times { simulate_request_cycle } }
end
lang-ruby

Memory Usage Monitoring

# Monitor memory usage
require 'objspace'

def memory_usage
  ObjectSpace.count_objects
end

# Track memory before and after jemalloc
puts "Memory objects: #{memory_usage[:TOTAL]}"
lang-ruby

Monitoring in Production

NewRelic Integration

Rails Blueprint includes NewRelic configuration to monitor performance improvements:
# config/newrelic.yml
production:
  app_name: <%= Rails.application.class.module_parent_name %>
  license_key: <%= Rails.application.credentials.newrelic_license_key %>

  # Track Ruby performance metrics
  ruby_vm_stats:
    enabled: true

  # Monitor garbage collection
  gc_profiler:
    enabled: true
lang-yaml

Health Endpoint

Monitor performance through the built-in health endpoint:
curl https://yourapp.com/health
lang-bash{
  "status": "ok",
  "version": {
    "basic": "1.2.0"
  },
  "git_revision": "c011f46f988ea5421454b3897e4b29c14a09861b",
  "timestamp": "2025-07-16T10:27:04Z"
}
lang-json

Common Issues and Solutions

YJIT Compilation Issues

Problem: YJIT not available after installation
Solution: Ensure Rust is installed and --enable-yjit flag was used
# Check if YJIT is compiled in
ruby -e "puts RubyVM::YJIT.respond_to?(:enable)"
# Should return: true
lang-bash

jemalloc Loading Issues

Problem: jemalloc not being loaded in production
Solution: Verify LD_PRELOAD is set correctly
# Check if jemalloc is loaded
lsof -p $ | grep jemalloc
lang-bash

Memory Leak Detection

Problem: Memory usage still growing despite jemalloc
Solution: Use memory profiling tools
# Add to Gemfile for debugging
group :development do
  gem 'memory_profiler'
  gem 'rack-mini-profiler'
end
lang-ruby

Ruby 3.4 Compatibility

Removed Standard Libraries

Ruby 3.4 removed some standard libraries. Rails Blueprint includes necessary gems:
# Gemfile additions for Ruby 3.4 compatibility
gem 'csv'      # Removed from Ruby 3.4 stdlib
gem 'observer' # Removed from Ruby 3.4 stdlib
lang-ruby

Performance Improvements in 3.4

Ruby 3.4 includes additional performance improvements:
  • PRISM parser - New default parser with better performance
  • Improved garbage collection - More efficient object allocation
  • Better memory layout - Optimized object structure

Future Considerations

Ruby 3.5 and Beyond

Keep an eye on upcoming Ruby versions:
  • Enhanced YJIT - Continued JIT improvements
  • Better memory management - Further allocation optimizations
  • Concurrency improvements - Better thread and fiber performance

Application-Specific Optimizations

Consider these Rails Blueprint-specific optimizations:
Command Pattern: Structure business logic to benefit from JIT compilation
Component Architecture: Use view components for better memory efficiency
Background Jobs: Optimize job processing with Good Job and PostgreSQL

Conclusion

Ruby 3.4.4 with YJIT and jemalloc represents a significant performance leap for Rails applications. Rails Blueprint makes these optimizations accessible from day one, delivering:
  • 15-30% performance improvements through YJIT
  • 10-20% memory usage reduction with jemalloc
  • Production-ready configuration out of the box
  • Monitoring and verification tools included
The performance benefits compound over time as YJIT learns your application's hot paths and jemalloc optimizes memory allocation patterns. Combined with Rails Blueprint's modern architecture (Hotwire, ViewComponent, Good Job), you get a Rails application that's not just feature-complete but performance-optimized.
Ready to experience these performance improvements? Get started with Rails Blueprint and feel the difference optimized Ruby can make.

Start creating your next app now