Slide 1

Slide 1 text

JRuby Everywhere! Bringing Ruby to Desktop, Mobile, and Server with the JVM

Slide 2

Slide 2 text

Hello! • Charles Oliver Nutter • @headius(@mastodon.social) • [email protected] • Full-time JRuby dev since 2006 • Founder of Headius Enterprises • "The JRuby Company"

Slide 3

Slide 3 text

Thank You! • eazyBI is a JRuby user! • World-class reporting and business intelligence • Integration with the whole Atlassian stack • I'm here because of them! • Sponsor of Baltic Ruby • Sponsor of my trip to Riga

Slide 4

Slide 4 text

What is Ruby?

Slide 5

Slide 5 text

CRuby/MRI

Slide 6

Slide 6 text

Rails

Slide 7

Slide 7 text

Ruby != CRuby + Rails

Slide 8

Slide 8 text

Programming Language

Slide 9

Slide 9 text

Do you want to do more Ruby?

Slide 10

Slide 10 text

Do you want to build other things with Ruby?

Slide 11

Slide 11 text

Do you want Ruby to run faster, scale larger, and do more?

Slide 12

Slide 12 text

You Need JRuby!

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

What is JRuby? • Ruby on the Java Virtual Machine • Ruby implementation fi rst, JVM language second • Many bene fi ts from JVM ecosystem • Ruby code should "just work" • JVM-based extensions (no need for C build tools) • Parallel threading (no need to fork) • Thousands of production users, 18 years of real-world use

Slide 15

Slide 15 text

Leading the Way! • JRuby has already solved many Ruby challenges! • YJIT/ZJIT: JRuby added JIT in 2008 • Deoptimization, saving JIT code: JVMs already can do this • Class.new optimization: since 2015 • Namespaces for isolation: always! • We move fast and help solve Ruby's problems!

Slide 16

Slide 16 text

JRuby = Opportunity

Slide 17

Slide 17 text

World of Opportunities • Large enterprises on JVM • Java, Kotlin, Scala, Clojure • Mobile platforms built on Android • TVs, POS, control terminals • Desktop applications • 68% of of fi ce work done on desktops

Slide 18

Slide 18 text

Getting Started

Slide 19

Slide 19 text

JRuby Install • Install a Java runtime • Java 21+ required for JRuby 10 • Latest Java recommended (Java 24) • Install JRuby • Recommended: Ruby installer, system package, Docker image • Download tarball/zip, Windows installer, build yourself

Slide 20

Slide 20 text

$ java -version openjdk version "24" 2025-03-18 OpenJDK Runtime Environment (build 24+36-3646) OpenJDK 64-Bit Server VM (build 24+36-3646, mixed mode, sharing) $ ruby-install jruby-10.0.0.1 >>> Updating jruby versions ... >>> Installing jruby 10.0.0.1 into /Users/headius/.rubies/jruby-10.0.0.1 ... >>> Downloading https://repo1.maven.org/maven2/org/jruby/jruby-dist/10.0.0.1/jruby- dist-10.0.0.1-bin.tar.gz into /Users/headius/src ... % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 32.1M 100 32.1M 0 0 7887k 0 0:00:04 0:00:04 --:--:-- 7887k >>> Verifying jruby-dist-10.0.0.1-bin.tar.gz ... >>> Extracting jruby-dist-10.0.0.1-bin.tar.gz to /Users/headius/src/jruby-10.0.0.1 ... >>> Installing jruby 10.0.0.1 ... >>> Symlinking bin/ruby to bin/jruby ... >>> Successfully installed jruby 10.0.0.1 into /Users/headius/.rubies/jruby-10.0.0.1

Slide 21

Slide 21 text

$ java -version openjdk version "24" 2025-03-18 OpenJDK Runtime Environment (build 24+36-3646) OpenJDK 64-Bit Server VM (build 24+36-3646, mixed mode, sharing) $ ruby-install jruby-10.0.0.1 >>> Updating jruby versions ... >>> Installing jruby 10.0.0.1 into /Users/headius/.rubies/jruby-10.0.0.1 ... >>> Downloading https://repo1.maven.org/maven2/org/jruby/jruby-dist/10.0.0.1/jruby- dist-10.0.0.1-bin.tar.gz into /Users/headius/src ... % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 32.1M 100 32.1M 0 0 7887k 0 0:00:04 0:00:04 --:--:-- 7887k >>> Verifying jruby-dist-10.0.0.1-bin.tar.gz ... >>> Extracting jruby-dist-10.0.0.1-bin.tar.gz to /Users/headius/src/jruby-10.0.0.1 ... >>> Installing jruby 10.0.0.1 ... >>> Symlinking bin/ruby to bin/jruby ... >>> Successfully installed jruby 10.0.0.1 into /Users/headius/.rubies/jruby-10.0.0.1

Slide 22

Slide 22 text

[] ~ $ irb >> RUBY_VERSION => "3.4.2" >> JRUBY_VERSION => "10.0.1.0" >> runtime = java.lang.Runtime.runtime => # >> runtime.available_processors => 8 >> runtime.free_memory => 91420584

Slide 23

Slide 23 text

Ruby Compatibility • JRuby 10 supports Ruby 3.4 • Language and core specs: 98% passing • Pure-Ruby standard library shared with CRuby • JRuby support for most native stdlib • JRuby 9.4 supports Ruby 3.1 • Maintained until April 2026

Slide 24

Slide 24 text

JVM Libraries

Slide 25

Slide 25 text

A Whole New World • JVM ecosystem has tens of thousands of libraries • Graphics, GUIs, servers, document formats, AI/LLM • One of the largest collections in the dev world • All available to JRuby users! • Easy integration into Ruby apps and code

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

No content

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

Example: 3D Bar Chart with JFreeChart • Chart-generating library with support for document output • Easily used from JRuby • https://blog.headius.com/ 2025/05/3d-charts-and-more-with- jruby-and-jfreechart.html

Slide 31

Slide 31 text

Dependencies • Jar fi le, like Gem fi le • Like Bundler's Gem fi le • Maven "coordinates" • lock_jars command • Like bundle install jar 'org.jfree:jfreechart:1.5.5' jar 'org.jfree:org.jfree.chart3d:2.1.0' $ lock_jars -- jar root dependencies -- org.jfree:jfreechart:1.5.5:compile org.jfree:org.jfree.chart3d:2.1.0:compile org.jfree:org.jfree.svg:5.0.6:compile org.jfree:org.jfree.pdf:2.0:compile Jars.lock updated Jar fi le

Slide 32

Slide 32 text

java_import org.jfree.chart3d.data.StandardCategoryDataset3D java_import org.jfree.chart3d.data.DefaultKeyedValues ... require 'json' data = JSON.load(File.read("data/app_monitoring_revenue.json")) dataset = StandardCategoryDataset3D.new data.each do |name, subset| values = DefaultKeyedValues.new subset.each { values.put(_1, _2) } dataset.add_series_as_row name, values end

Slide 33

Slide 33 text

java_import org.jfree.chart3d.data.StandardCategoryDataset3D java_import org.jfree.chart3d.data.DefaultKeyedValues ... require 'json' data = JSON.load(File.read("data/app_monitoring_revenue.json")) dataset = StandardCategoryDataset3D.new data.each do |name, subset| values = DefaultKeyedValues.new subset.each { values.put(_1, _2) } dataset.add_series_as_row name, values end

Slide 34

Slide 34 text

java_import org.jfree.chart3d.data.StandardCategoryDataset3D java_import org.jfree.chart3d.data.DefaultKeyedValues ... require 'json' data = JSON.load(File.read("data/app_monitoring_revenue.json")) dataset = StandardCategoryDataset3D.new data.each do |name, subset| values = DefaultKeyedValues.new subset.each { values.put(_1, _2) } dataset.add_series_as_row name, values end

Slide 35

Slide 35 text

java_import org.jfree.chart3d.data.StandardCategoryDataset3D java_import org.jfree.chart3d.data.DefaultKeyedValues ... require 'json' data = JSON.load(File.read("data/app_monitoring_revenue.json")) dataset = StandardCategoryDataset3D.new data.each do |name, subset| values = DefaultKeyedValues.new subset.each { values.put(_1, _2) } dataset.add_series_as_row name, values end

Slide 36

Slide 36 text

Create a 3D Bar Chart chart = Chart3DFactory.create_bar_chart( "Quarterly Revenues", "Application & Performance Monitoring Companies", dataset, nil, "Quarter", "$million Revenues") chart.chart_box_color = Color.new(255, 255, 255, 127) chart.legend_anchor = LegendAnchor::BOTTOM_RIGHT

Slide 37

Slide 37 text

Generate PNG width, height = 600, 500 category_chart_image = BufferedImage.new(width, height, BufferedImage::TYPE_INT_RGB) category_chart_graphics = category_chart_image.create_graphics chart.draw(category_chart_graphics, Rectangle.new(width, height)) category_chart_file = File.open("category_chart.png", "w") ImageIO.write(category_chart_image, "PNG", category_chart_file.to_outputstream)

Slide 38

Slide 38 text

Generate PNG width, height = 600, 500 category_chart_image = BufferedImage.new(width, height, BufferedImage::TYPE_INT_RGB) category_chart_graphics = category_chart_image.create_graphics chart.draw(category_chart_graphics, Rectangle.new(width, height)) category_chart_file = File.open("category_chart.png", "w") ImageIO.write(category_chart_image, "PNG", category_chart_file.to_outputstream)

Slide 39

Slide 39 text

Generate PNG width, height = 600, 500 category_chart_image = BufferedImage.new(width, height, BufferedImage::TYPE_INT_RGB) category_chart_graphics = category_chart_image.create_graphics chart.draw(category_chart_graphics, Rectangle.new(width, height)) category_chart_file = File.open("category_chart.png", "w") ImageIO.write(category_chart_image, "PNG", category_chart_file.to_outputstream)

Slide 40

Slide 40 text

Generate PNG width, height = 600, 500 category_chart_image = BufferedImage.new(width, height, BufferedImage::TYPE_INT_RGB) category_chart_graphics = category_chart_image.create_graphics chart.draw(category_chart_graphics, Rectangle.new(width, height)) category_chart_file = File.open("category_chart.png", "w") ImageIO.write(category_chart_image, "PNG", category_chart_file.to_outputstream)

Slide 41

Slide 41 text

No content

Slide 42

Slide 42 text

Render to SVG and PDF svg_graphics = SVGGraphics2D.new(width, height) svg_graphics.defs_key_prefix = "jruby_charts" chart.element_hinting = true chart.draw(svg_graphics, Rectangle.new(width, height)) svg = svg_graphics.get_svg_element chart.id File.write("category_chart.svg", svg) pdf_doc = PDFDocument.new pdf_doc.title = "Application & Performance Monitoring Companies Revenue" pdf_doc.author = "Charles Oliver Nutter"; page = pdf_doc.create_page(Rectangle.new(612, 468)) pdf_graphics = page.graphics2D chart.draw(pdf_graphics, Rectangle.new(0, 0, 612, 468)) File.write("category_chart.pdf", pdf_doc.pdf_bytes)

Slide 43

Slide 43 text

GUI Libraries • Swing, built into JDK • Clean, cross-platform, easy to build simple UIs • Scalable Windowing Toolkit (Eclipse SWT) • Native widgets, WebKit browser component, rich ecosystem • JavaFX (via JRubyFX, github/jruby/jrubyfx) • Scene-based, vector drawing, event-driven modern UI library

Slide 44

Slide 44 text

Shoes 4

Slide 45

Slide 45 text

Glimmer • Glimmer GUI DSL • Multiple backends (SWT, GTK, ...) • JRuby + SWT is the most mature • JRuby makes cross-platform GUI much easier! • Works same everywhere • GUI libraries shipped with gem • https://github.com/AndyObtiva/glimmer

Slide 46

Slide 46 text

JavaFX • Scene-based UI toolkit • Cross-platform including mobile • JRubyFX provides a Ruby wrapper • FXML layout or widget scripting • https://github.com/jruby/jrubyfx

Slide 47

Slide 47 text

Ruboto: JRuby on Android • ruboto.org, https://github.com/ruboto/ruboto • Actively used for commercial projects today • Build interface with GUI builder, wire it up with Ruby code • Neglected a bit but being updated for JRuby 10 now!

Slide 48

Slide 48 text

Ruboto IRB • IRB in JRuby on Android! • Plus an editor and script runner • Not currently in the store, but we will republish soon! • Search for "Ruboto Core" and "Ruboto IRB" APKs

Slide 49

Slide 49 text

Purugin for Minecraft event(:player_egg_throw) do |e| e.hatching = true e.num_hatches = 120 e.player.mesg "hatched" end

Slide 50

Slide 50 text

JVM Features

Slide 51

Slide 51 text

JVM GC • Many options to tune JVM GCs • Heap size: small or large? • Throughput: faster allocations or shorter pause times? • Working set: large in-memory or mostly new objects? • Many options in standard OpenJDK • Serial, Parallel, G1, ZGC, Shenandoah

Slide 52

Slide 52 text

JVM JIT • HotSpot JIT is most widely deployed • C1 "client" JIT: fast, simple optimizations • C2 "server" JIT: needs pro fi le data, heavy optimization • Both enabled with various "tiers" of JIT • Graal JIT: newer, more aggressive optimizations • OpenJ9 JIT (IBM JDK): of fl ine AOT, JIT servers, CRIU

Slide 53

Slide 53 text

Monitoring and Pro fi ling • VisualVM: GUI console for basic JVM monitoring • JDK Flight Recorder: always-on monitoring with pro fi ling options • Low-overhead 1% to 5%, built into OpenJDK • JDK Mission Control: GUI client for Flight Recorder data • https://adoptium.net/jmc/

Slide 54

Slide 54 text

Visual VM

Slide 55

Slide 55 text

JDK Mission Control

Slide 56

Slide 56 text

No content

Slide 57

Slide 57 text

Performance and Scaling

Slide 58

Slide 58 text

Optimizing Performance • JRuby depends on JVM for most optimization • JVM has world-class JIT compilers, garbage collectors • Similar design to ZJIT, but with 30 years of work in it • JRuby itself has an IR, basic block-based interpreter and JIT • Similar design to ZJIT starting in JRuby 9.0 (2015)

Slide 59

Slide 59 text

JRuby Architecture Ruby (.rb) JIT Java Instructions (java bytecode) Ruby Instructions (IR) parse interpret interpreter interpret C1 compile native code better native code java bytecode interpreter execute C2 compile Java Virtual Machine JRuby Internals

Slide 60

Slide 60 text

Pure Ruby red/black tree 0 20 40 60 80 red/black bench iters/s 76.8 iters/sec 65.5 iters/sec 15.1 iters/sec CRuby 3.4 CRuby 3.4 YJIT JRuby 10

Slide 61

Slide 61 text

Class#new Optimization • CRuby adding opt_new to optimize Class#new • Using C code to allocate + initialize hurts performance • JRuby implemented this in 2016 • If Class#new is default, allocate and call #initialize inline • Allocation and initialize both inline back to caller

Slide 62

Slide 62 text

Object.new

Slide 63

Slide 63 text

0M 3.5M 7M 10.5M 14M Object.new allocations per second 0M 13M 10.3M Ruby 3.4 + YJIT Ruby 3.5 + opt_new JRuby 10

Slide 64

Slide 64 text

0M 75M 150M 225M 300M Object.new allocations per second 217.5M 13M 10.3M Ruby 3.4 + YJIT Ruby 3.5 + opt_new JRuby 10 🤯

Slide 65

Slide 65 text

1M 10M 100M 1000M 10000M Object.new allocations per second 217.5M 13M 10.3M Ruby 3.4 + YJIT Ruby 3.5 + opt_new JRuby 10

Slide 66

Slide 66 text

class Foo def initialize = nil end

Slide 67

Slide 67 text

1M 10M 100M Object.new allocations per second Foo.new allocations per second 216M 217.5M 22.1M 13M 10.3M 10.3M Ruby 3.4 + YJIT Ruby 3.5 + opt_new JRuby 10

Slide 68

Slide 68 text

Allocation Pro fi le

Slide 69

Slide 69 text

Alternative JITs • HotSpot JIT • Standard JIT compiler in OpenJDK • Best balance of performance, reliability • Graal JIT • Part of GraalVM project • Advanced optimizations, sometimes unpredictable perf

Slide 70

Slide 70 text

0M 750M 1500M 2250M 3000M Foo.new allocations per second 2,591.1M 216M JRuby 10 JRuby 10 + Graal 🤯

Slide 71

Slide 71 text

respond_to? • Commonly used for duck-typing • obj.to_str if respond_to?(:to_str) • Literal symbol form is by far the most common • Currently compiled as a call to respond_to? • respond_to? then does a method lookup every time • Can we do better?

Slide 72

Slide 72 text

foo if respond_to?(:foo)

Slide 73

Slide 73 text

respond_to? :foo invoke respond_to? :foo respond_to? method cached? look up respond_to? method invoke respond_to? method look up :foo cache respond_to? method return true or false is :foo implemented?

Slide 74

Slide 74 text

respond_to? :foo invoke respond_to? :foo respond_to? method cached? look up respond_to? method invoke respond_to? method look up :foo cache respond_to? method return result is :foo implemented? respond_to? :foo result cached? cache respond_to? :foo

Slide 75

Slide 75 text

0M 20M 40M 60M 80M foo if respond_to?(:foo) 70.8M 22.3M 16.8M CRuby 3.4 + YJIT JRuby 10 JRuby 10.next

Slide 76

Slide 76 text

Scaling Applications • Classic problem on CRuby/MRI • No concurrent threads, so we need worker processes • Processes duplicate runtime state and waste resources • JRuby is a good solution • Multi-threaded single process runs your entire site • Single process with leading-edge GC uses resources better

Slide 77

Slide 77 text

Baseline Rails App • Scaffolded "blog" application on PostgreSQL, Puma • IBM VPC instance: 8 vCPU, 32GB • CRuby 3.2, 16 workers • JRuby 9.4: 16 threads • Database, siege benchmark driver on same instance

Slide 78

Slide 78 text

Requests per second 0 450 900 1350 1800 60s siege iteration 1 2 3 4 5 6 7 JRuby CRuby CRuby + YJIT

Slide 79

Slide 79 text

requests per second (higher is better) 0rps 450rps 900rps 1350rps 1800rps 1,705rps 1,550rps 1,280rps CRuby CRuby + YJIT JRuby

Slide 80

Slide 80 text

Memory • JRuby tuned with 300MB heap: 955MB RSS • Additional users only require a little more memory • CRuby single-user: 103MB RSS • Additional users eventually need additional processes • Even perfect Copy-On-Write still duplicates live data, JIT, GC

Slide 81

Slide 81 text

Memory Required 0MB 2000MB 4000MB 6000MB 8000MB 1 user 16 users 160 users JRuby CRuby

Slide 82

Slide 82 text

Optimizing Concurrency

Slide 83

Slide 83 text

Threads or Ractors? • Ractors are designed to bring concurrency to Ruby • You must write Ractor-friendly code • High overhead crossing Ractor boundary • Threads in JRuby are already 100% concurrent • You must right Thread-friendly code • But zero overhead due to shared object • Thanks to Maciej Mensfeld for the benchmark

Slide 84

Slide 84 text

if THREADS queues = CONCURRENT.times.map { Queue.new } threads = CONCURRENT.times.map do |i| Thread.new do queue = queues[i] data_chunk = queue.pop data_chunk.each { |item| JSON.parse(item) } end end end

Slide 85

Slide 85 text

Threads vs Ractors 0 1.25 2.5 3.75 5 times faster than linear 4.2x 1.2x 1.1x CRuby threads CRuby ractors JRuby threads

Slide 86

Slide 86 text

JRuby Future

Slide 87

Slide 87 text

JRuby 10.x • Many more optimizations coming! • Now that we are 3.4 compatible, we can work on fun stuff • If you fi nd something that's slow, let us! • More JDK features • JVM features to improve startup, fast native library integration • Upgrade JVM, your JRuby code runs faster!

Slide 88

Slide 88 text

JRuby and You • Building an app? Try JRuby! • bundle install, run on Puma • Try out some JVM libraries • Library author? Test on JRuby! • We to support your project! • Let's talk!

Slide 89

Slide 89 text

JRuby Development • Funded by Headius Enterprises LLC • JRuby support: updates, bugs, security • JVM, Ruby support: pro fi ling, optimization • Partner with us! • User support • GitHub sponsorships • Try JRuby out, fi le issues, push PRs

Slide 90

Slide 90 text

Thank You! • Charles Oliver Nutter • @headius • @mastodon.social • .bsky.social • [email protected] • Stickers on the sticker table! • Help me keep working on JRuby!