JRuby - Ruby on the JVM
Master JRuby, the Ruby implementation that runs on the Java Virtual Machine, enabling true multi-threading and seamless Java integration.
What is JRuby?
JRuby is a Ruby implementation that runs on the Java Virtual Machine (JVM), written primarily in Java with some Ruby code.
Key Characteristics
- JVM-Based: Leverages the mature Java Virtual Machine
- No Global Interpreter Lock: True multi-threading support
- Java Integration: Direct access to Java libraries and frameworks
- JIT Compilation: Benefits from JVM's Just-In-Time compiler
- Garbage Collection: Uses JVM's advanced GC algorithms
- Cross-Platform: Runs anywhere the JVM runs
Installation & Setup
Prerequisites
# Java 8+ required
java -version
# Download JRuby from jruby.org
# or use package managers
# macOS with Homebrew
brew install jruby
# Ubuntu/Debian
apt-get install jruby
# Using rbenv
rbenv install jruby-9.4.0.0
rbenv global jruby-9.4.0.0
Version Information
# Check JRuby version
jruby -v
# jruby 9.4.0.0 (3.1.0) 2022-12-08 1a2b16698d OpenJDK 64-Bit Server VM 11.0.17+8
# Ruby compatibility level
puts RUBY_VERSION # Shows Ruby compatibility (e.g., "3.1.0")
# Check JRuby-specific info
puts JRUBY_VERSION # JRuby version
puts RUBY_ENGINE # "jruby"
puts RUBY_PLATFORM # Platform info
True Multi-Threading (No GIL)
Major Advantage: JRuby has no Global Interpreter Lock, enabling true parallel execution of Ruby threads.
Parallel Processing Example
require 'benchmark'
# CPU-intensive task
def fibonacci(n)
return n if n <= 1
fibonacci(n - 1) + fibonacci(n - 2)
end
# Compare sequential vs parallel execution
Benchmark.bm(20) do |x|
x.report("Sequential:") do
4.times { fibonacci(35) }
end
x.report("Parallel (4 threads):") do
threads = []
4.times do
threads << Thread.new { fibonacci(35) }
end
threads.each(&:join)
end
end
# JRuby will show significant parallel speedup
# MRI will show little to no improvement due to GIL
Thread Safety Considerations
- Shared State: Must be carefully synchronized
- Thread-Safe Libraries: Ensure gems are thread-safe
- Java Concurrency: Can use Java's concurrent utilities
- Race Conditions: More likely than in MRI due to true parallelism
Java Integration
JRuby provides seamless integration with Java classes, libraries, and frameworks.
Using Java Classes
# Import Java classes
java_import 'java.util.ArrayList'
java_import 'java.io.File'
java_import 'javax.swing.JFrame'
# Or use Java:: namespace
list = Java::JavaUtil::ArrayList.new
file = Java::JavaIo::File.new("example.txt")
# Direct instantiation
frame = javax.swing.JFrame.new("My Window")
# Java methods in Ruby style
list.add("Hello")
list.size()
list.isEmpty() # Java method
list.empty? # Ruby-style alias
Java Collections Integration
# Java collections work with Ruby enumerable methods
java_list = java.util.ArrayList.new
java_list.add(1)
java_list.add(2)
java_list.add(3)
# Use Ruby enumerable methods
java_list.map { |x| x * 2 } # [2, 4, 6]
java_list.select(&:even?) # []
# Convert between Ruby and Java collections
ruby_array = [1, 2, 3]
java_list = ruby_array.to_java
ruby_array_back = java_list.to_a
Implementing Java Interfaces
# Implement Java interfaces in Ruby
java_import 'java.util.Comparator'
# Create a Comparator
string_comparator = Java::JavaUtil::Comparator.impl do |proxy|
def compare(a, b)
a.length <=> b.length
end
end
# Use with Java collections
words = ["hello", "hi", "world", "a"].to_java
java.util.Arrays.sort(words, string_comparator)
puts words.to_a # ["a", "hi", "hello", "world"]
JRuby Extensions & Libraries
JRuby-Specific Gems
# Gemfile for JRuby projects
source 'https://rubygems.org'
gem 'jruby-openssl' # SSL support
gem 'warbler' # Package as WAR files
gem 'torquebox' # Application server
gem 'jdbc-mysql' # JDBC MySQL driver
gem 'jruby-rack' # Rack adapter for Java containers
# Platform-specific gems
gem 'some-gem', platform: :jruby
gem 'different-gem', platform: :ruby # MRI only
JDBC Database Access
require 'java'
require 'jdbc/mysql'
# Load JDBC driver
java_import 'java.sql.DriverManager'
# Connect to database
url = "jdbc:mysql://localhost:3306/mydb"
conn = DriverManager.getConnection(url, "user", "password")
# Execute queries
stmt = conn.createStatement()
rs = stmt.executeQuery("SELECT * FROM users")
while rs.next()
puts "User: #{rs.getString('name')}"
end
# ActiveRecord with JDBC
# config/database.yml
# adapter: jdbcmysql
# url: jdbc:mysql://localhost:3306/mydb
Java Web Integration
# Deploy Rails app as WAR file using Warbler
# Gemfile
gem 'warbler'
# warble config
bundle exec warble config
# Create WAR file
bundle exec warble
# Deploy to Tomcat, JBoss, etc.
# myapp.war can run in any Java application server
# Access Java servlet context
if defined?(JRuby)
servlet_context = $servlet_context
real_path = servlet_context.getRealPath("/")
end
Performance Characteristics
Strengths
- True Parallelism: No GIL limitations for CPU-intensive tasks
- JIT Compilation: JVM's optimizing compiler improves long-running performance
- Mature GC: Advanced garbage collection algorithms
- Memory Management: Efficient heap management
- Concurrent Libraries: Access to Java's concurrent utilities
- Warmup Performance: Gets faster over time with JIT optimization
Considerations
- Startup Time: Slower startup due to JVM initialization
- Memory Overhead: Higher initial memory usage
- C Extension Compatibility: Cannot use native C extensions
- JVM Dependency: Requires Java runtime environment
- Cold Performance: May be slower initially before JIT optimization
Deployment Options
Traditional Ruby Deployment
# Use like regular Ruby
jruby -S gem install rails
jruby -S rails new myapp
cd myapp
jruby -S bundle install
jruby -S rails server
# Puma with JRuby
# config/puma.rb
workers 0 # Use threads instead of workers
threads 16, 32 # Take advantage of true threading
WAR File Deployment
# Gemfile
gem 'warbler'
# Configure warbler
bundle exec warble config
# config/warble.rb
Warbler::Config.new do |config|
config.features = %w(executable)
config.jar_name = "myapp"
end
# Create executable WAR
bundle exec warble
# Deploy to Tomcat, JBoss, WebLogic, etc.
# Can also run standalone:
java -jar myapp.war
TorqueBox Application Server
# Gemfile
gem 'torquebox', '~> 4.0.0'
# config/torquebox.yml
web:
context: /myapp
environment:
RAILS_ENV: production
# Features:
# - Web application hosting
# - Background job processing
# - Scheduled jobs
# - Message queues
# - Clustering
Migration from MRI
Compatibility Checklist
- C Extensions: Replace with Java alternatives or pure Ruby
- Thread Safety: Review for race conditions
- File I/O: May behave differently on Windows
- Memory Usage: Monitor for different patterns
- Performance: Benchmark critical paths
Common Migration Issues
# Check for problematic gems
# Replace C extensions:
# nokogiri -> java-based XML processing
# json -> built-in JSON support
# bcrypt -> jBCrypt
# mysql2 -> jdbc-mysql
# Detect runtime
if RUBY_ENGINE == 'jruby'
# JRuby-specific code
require 'java'
else
# MRI-specific code
require 'some_c_extension'
end
Migration Testing
# Test both implementations
# .github/workflows/test.yml
strategy:
matrix:
ruby: [ruby-3.1, jruby-9.4.0.0]
# Gemfile platform-specific dependencies
gem 'mysql2', platform: :ruby
gem 'jdbc-mysql', platform: :jruby
# CI testing with both implementations
script:
- bundle exec rspec
JRuby Best Practices
Threading Best Practices
- Use Thread Pools: Avoid creating too many threads
- Synchronize Shared State: Use Mutex, java.util.concurrent
- Thread-Safe Gems: Verify all dependencies are thread-safe
- Monitor Performance: Profile multi-threaded code carefully
JVM Tuning
# JVM options for JRuby
export JRUBY_OPTS="-J-Xmx2g -J-Xms512m"
export JAVA_OPTS="-server -XX:+UseG1GC"
# For production
JRUBY_OPTS="-J-Xmx4g -J-XX:+UseG1GC -J-XX:MaxGCPauseMillis=100"
# Enable JIT compilation
JRUBY_OPTS="$JRUBY_OPTS --jit.threshold=50"
Memory Management
- Heap Sizing: Set appropriate -Xmx and -Xms values
- GC Algorithm: Choose GC suited for your workload
- Memory Monitoring: Use JVM tools like JVisualVM
- Leak Detection: Monitor for memory leaks in long-running processes
When to Choose JRuby
Ideal Use Cases
- CPU-Intensive Applications: Parallel processing, mathematical computing
- Enterprise Integration: Existing Java infrastructure
- High-Concurrency Applications: Need true multi-threading
- Java Library Access: Leverage existing Java libraries
- Application Server Deployment: WAR files in Java containers
- Long-Running Processes: Benefit from JIT compilation
Consider Alternatives For
- C Extension Heavy: Applications relying on native extensions
- Fast Startup Required: CLI tools, short-lived scripts
- Memory Constrained: Very limited memory environments
- Simple I/O Applications: MRI may be sufficient