00:00:08
Okay — I'm excited you're all here. I'll get right into it; we have a lot to cover about JRuby 10 and some of the optimizations we're working on.
00:00:19
First, thanks to my friend Mufan: I want to promote RubyConf Taiwan 2025 combined with KUSKUP, the open-source conference in Taiwan. If you want to know more, find him — he'll tell you everything. I hope to see many of you there.
00:00:40
Konnichiwa — I'm very excited to be back here to present JRuby 10. Here's my basic contact information. I've been developing and maintaining JRuby for 20 years.
00:00:54
I've tried to bring the best of the Java platform to the Ruby world. Last year I'm listed as unemployed because Red Hat, which sponsored the project for 12 years, moved on. Right now I work for the Ruby community: I continue JRuby development full-time and fund the project through sponsorships and support contracts for organizations running critical production applications.
00:01:41
So far, sponsorship and support contracts have allowed me to maintain the project as a one-person core team, but I'm looking for new partnerships. If you can help the project or need help deploying or profiling code, please reach out. The big announcement this week is that we finally released JRuby 10 — our biggest release in many years. We've jumped to Ruby 3.4 compatibility and added thousands of new tests and assertions to improve compatibility and stability.
00:02:26
We now require a minimum of Java 21 so we can take advantage of modern JVM features; running on older Java versions made it too hard to utilize them. There's a lot of additional performance work planned for this year. "Optimizing JRuby" covers many topics: compatibility, startup and warm-up time (traditionally hard on the JVM), straight-line performance, and multi-core scalability. I'll go through each of these areas and show what we're doing in JRuby.
00:03:17
Tracking Ruby updates and maintaining compatibility is a challenging, ongoing project. The big leap to JRuby 10 (supporting Ruby 3.4) means we've finally caught up with CRuby again. On a chart of supported versions we sometimes fall behind, miss a release or two, and then catch up — that's the nature of balancing compatibility work against other development.
00:04:05
Another view is days of lag: historically JRuby has lagged CRuby by varying amounts, but the 3.4 block is the smallest — the closest we've ever been to being on schedule. We could have released JRuby 10 in December, but we finished more stabilization and testing to get as many green tests as possible. We're very excited to be caught up with CRuby.
00:04:58
All of this compatibility work is done by the JRuby team, with some external contributors. CRuby has a large list of contributors; JRuby is a much smaller team. Tom does a lot of parser and user-support work. My son, in the audience, is the project's third-largest committer — he implemented the new JRuby launcher and helped with some C/native code. We need more contributors who can test, implement features (in Ruby or otherwise), and help us keep up with Ruby releases.
00:06:06
What about Rails 8 compatibility? Generally, any pure-Ruby parts of Rails should work when JRuby supports the required Ruby version. Rails 8 requires Ruby 3.2; JRuby 10 supports Ruby 3.4, so Ruby-based parts of Rails ought to work. In practice, most of the work to support Rails is updating our ActiveRecord implementation: database adapters contain a lot of special-case code that must be translated to the Java database APIs. That's where most of the lag comes from. If you're familiar with ActiveRecord adapters, we'd love help keeping up on the JRuby side.
00:07:15
Startup is one of the first issues people notice with JRuby, and first impressions matter. We want JRuby to feel as close to CRuby as possible as a development experience. Unfortunately there are many steps to get JRuby running: code is parsed and loaded, the JVM runs it for a while, and eventually the JVM optimizes it to native code. That whole process takes time, so we've explored new JDK features to speed up startup and warm-up. JRuby's internal IR is similar to the zjit IR coming to CRuby, and the JVM itself uses a similar SSA compiler design. Our goal is to shorten the time from source to fast native code.
00:08:41
On the JVM side, we've been examining exciting projects. First is the Application Class Data Sharing (App CDS) feature, which arrived around Java 17. It allows the JVM to preload and pre-cache parsed code, metadata, methods, and class data so we don't have to reparse everything at startup. App CDS has been a primary source of recent startup improvements.
00:09:12
Looking further ahead, Java 24 includes previews of machine-code caching (Project Leaden) that provide an AOT cache of optimized native code. Ideally, when the JVM generates optimized native code we won't need to repeat that work on restart: we can save off the optimized code and resume quickly. There's also an unusual approach called Coordinated Restore at Checkpoint (Project CRaC), which can save an entire process's memory and execution state and restore it rapidly. CRaC currently only supports Linux and has limitations, but it can dramatically improve startup.
00:10:02
To compare: CRuby is optimized for developer happiness and startup speed, so it's very quick to get going. Plain JRuby 9.4 was noticeably slower. There are flags (for example, -d--dev) to reduce optimizations and improve developer experience, but JRuby 10 — using App CDS — brings the baseline much closer to CRuby. Combined with early AOT-cache experiments and CRaC, we are getting back under a second for simple startup cases. Many engineers across the JDK community are working on these technologies.
00:11:29
Now let's talk about straight-line performance: making Ruby code run fast. JRuby relies heavily on the JVM to do the heavy lifting. The JVM uses an SSA compiler design similar to zjit but with decades of development and many engineers improving it. JRuby has its own IR and basic-block compilation that we feed to the JVM, and we try to give the JVM enough information to optimize Ruby well. In a pure-Ruby red-black tree benchmark, CRuby 3.4 with YJIT could do about 15 iterations per second. YJIT improves CRuby a lot, but JRuby can still be faster on many pure-Ruby workloads because the JVM's optimizer is very powerful.
00:12:58
Native C extensions are a barrier to some optimizations on CRuby: the VM can't always optimize across C extension boundaries. JRuby can often do better because ports of extensions to Java can be inlined with Ruby code and optimized together by the JVM. For example, the OJ JSON parser port runs faster on JRuby because the JVM can optimize the combined code at runtime better than standalone C code.
00:14:03
Fibers have become increasingly important in the Ruby community, but older JVMs made them difficult to support efficiently. Previously, JRuby implemented fibers by wrapping full native threads, which are heavy and limited in number per process. Project Loom on OpenJDK brought lightweight fibers to the JVM as virtual threads. In a benchmark that spins up a thousand fibers and resumes them, the native-thread version achieved about 49 iterations per second; the virtual-thread implementation on the JVM is many times faster. With some additional JRuby work to reduce fiber-creation overhead, I expect JRuby fibers on the JVM to be much faster, likely faster than CRuby soon.
00:15:36
Aaron Patterson presented optimizations for class allocation (inlining allocate and initialize). JRuby implemented a similar optimization almost ten years ago: the JVM can inline allocate/initialize sequences and optimize them strongly. That helps make object allocation very cheap on the JVM. We run object-allocation microbenchmarks: CRuby 3.4 with YJIT does around 10.3 million allocations per second; Aaron's opt-new patch adds ~30% (about 13 million/s). JRuby 10 can be significantly faster because the JVM inlines allocations and makes them low-cost.
00:16:58
I was surprised by how fast allocations were, so I attached a JVM profiler to ensure objects were actually being created. The profiler confirmed the objects are real, but the JVM's optimizations (escape analysis, allocation elimination) make the allocation path extremely cheap. Even when initialize is written in Ruby, JRuby's allocation performance sees little impact. The JVM's excellent garbage collectors also reclaim those objects efficiently. Because we've inlined allocation and initialization logic, newer JVM JITs such as Graal (Growl/JIT work) can eliminate temporary allocations entirely, improving throughput even further.
00:18:43
Some Ruby language features also help. The 'data' feature introduced in Ruby 3.2 provides an immutable, compact- storage struct-like type with a special constructor that allocates and initializes in one step. On a small benchmark allocating a data instance with three fields, CRuby 3.4 with YJIT gets about 2.9 million allocations per second. JRuby runs this nearly three times faster because the JVM optimizes those compact objects effectively.
00:19:57
Another area is concurrency. People talk about ractors as a way to use multiple cores in Ruby, but writing ractor-friendly code is challenging. Ractors require sharable, often frozen objects, and crossing ractor boundaries has high overhead. That overhead can negate concurrency benefits. In JRuby, if your code is thread-safe, you can use real threads to utilize all cores without major application changes. Threads in JRuby allow you to pass objects between threads and exploit multi-core systems in a single process.
00:21:07
We benchmarked threads versus ractors for a JSON-parsing workload. Threads are spun up to wait on a queue; when they receive work they parse JSON. In CRuby, threads give slight improvements but often don't fully exploit cores due to locks and other bottlenecks. Ractors can slow things down because of the boundary-crossing overhead (though recent patches have improved this to about 1.3x). On my four-core machine, JRuby using real threads shows the expected scaling: all cores light up and you can maximize parallel processing in a single process.
00:22:58
This translates to Rails applications as well. In CRuby, you need multiple processes to get concurrency, which multiplies memory usage, duplicative work, and database round trips. With JRuby you can often run your entire site in a single process, using less memory and getting better requests-per-second per megabyte of memory. That crossover point can make JRuby more resource efficient for concurrent web workloads.
00:24:08
JRuby also gives opportunities to use Ruby anywhere Java runs. You can take your Rails apps and libraries and deploy them in Java shops, package them as single binaries, and ship them as commercial software. At the conference I spoke with people who packaged Rails apps and deployed them in Java organizations without anyone realizing they were Ruby. As an example, using Java Swing from JRuby you can build a cross-platform GUI: create a frame, add a button, set an action listener to run Ruby code on click, and display the window with just a small Ruby program. Projects like Shoes 4 (JRuby-based) and Glimmer (with SWT) provide richer UI toolkits on JRuby.
00:25:44
There are many other integrations: JRuby supports Minecraft plugins written in Ruby, Android applications via frameworks like Rub, and other embedded use cases. Commercial users use JRuby on Android for point-of-sale devices, ad terminals, and other applications. JRuby runs wherever Java runs, which is nearly everywhere.
00:26:34
Looking forward to JRuby's future (10.x maintenance releases over the next year), we have many optimizations planned now that this release is out. There is a long list of improvements we can make: for example, keyword-argument handling still allocates a hash in JRuby. The JVM can remove some of those allocations, but we should avoid allocating a whole hash where it's not needed. That's one of the first items I'll be working on. If you find a case where JRuby is slower than CRuby, please tell me — it's likely something I can fix.
00:27:21
We'll continue to explore JDK features such as AOT cache to improve startup, Project Panama to make optimized native calls from Ruby (speeding up FFI), and other JVM advances. Often, upgrading the JVM delivers better garbage collectors, improved JITs, and cool new features that make your Ruby code run faster without changes. I need the Ruby community's help to keep JRuby healthy: JRuby can solve many Ruby community performance problems and open Ruby to difficult enterprise environments. If JRuby disappeared, Ruby would be measurably damaged. This is an opportunity to expand Ruby into a larger world of high-performance, high-concurrency processing while using the Ruby we love.
00:28:44
Please try JRuby and let me know how I can help: scaling, profiling, and optimizing your code. If you do use JRuby, let's talk about partnerships so I can help you run your application better and you can help the JRuby community stay healthy. I'll have JRuby office hours after this talk; come by with problems and I'll try to help. I brought some American IPAs and I also have stickers and business cards for anyone who wants them. Thank you.