Ruby Logo

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.

Home Ruby JRuby - Ruby on the JVM

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

Quick Navigation

Related Topics

Video Tutorial

Watch and learn jruby - ruby on the jvm

Pro Tip: After reading through the content above, watch this video to reinforce your understanding and see the concepts in action!