A weekly newsletter and podcast diving into Clojure programs and libraries by Daniel Compton.

54: JRuby with Charles Oliver Nutter

Tuesday, 17 September 2024

Download: MP3 - 00:54:15

Charles Oliver Nutter talks about JRuby, the JVM, JRuby 10, improving JRuby startup time, and going independent

Transcript

Daniel: Hello, welcome to The REPL, a podcast diving into Clojure programs and libraries. This week, I’m talking about building another language on the JVM with Charles Oliver-Nutter, a co-lead of JRuby. Welcome to the show, Charles.

Charles: Thank you.

Daniel Yeah, it’s great to have you on. We’re talking today about JRuby, which is obviously not Clojure, but I kind of think of JRuby as a bit of a sibling language. They both run on the JVM and are not the primary languages that run on the JVM. So I think there are probably some really interesting things that Clojure developers might be interested in, as well as non-Clojure developers who are interested in JRuby. So first off, tell us, what is JRuby?

Charles: The simple answer to JRuby is that it is Ruby running on top of the JVM. And of course, that means it’s a JVM language like any other. You can call into all the same libraries, deploy on the same servers, and use the same tooling. But when we talk to Ruby folks, we really frame it more as the JVM for Ruby. It’s a full-featured Ruby runtime. All of the standard Ruby command-line tools, the standard Ruby web frameworks, database frameworks, etc., all work the same way they do on the standard implementation, but with the added potential of using JVM native threading, JVM garbage collectors, and deploying on exotic platforms. So it really depends on your perspective. It’s either a JVM language that is Ruby, or it is a better runtime for the Ruby ecosystem.

Daniel: Right. Do you have a sense of which of those is the more dominant reason why someone would use JRuby, or is it fairly evenly split?

Charles: I would say it is actually a pretty even split. We have a number of folks who’ve come to JRuby from the Ruby world. Oftentimes, they reach a scaling limitation with their Ruby application, or they want to be able to deploy it in large enterprises that are a little reluctant to try some new language or new runtime. And it works out great for them. They do large-scale production deployments. There are thousands of users around the world that run JRuby to scale Ruby applications up. On the JVM side, we also see a number of folks who use JRuby’s JVM integration and scripting capabilities to build plugin subsystems. For example, plugin systems like EasyBI that run within Confluence or Jira, allowing you to use Ruby to do all of the integration logic behind the scenes. So we really fit in everywhere in that spectrum, and we tailor the work on JRuby towards the needs that folks have on each end.

Daniel: Right. Nice. And what are the guiding principles of JRuby in terms of how it differs from CRuby, or perhaps doesn’t differ from CRuby?

Charles: The biggest guiding principle is always compatibility. We try to make sure that Ruby, as imagined in JRuby, matches behaviorally, matches at the command line, and matches the whole development experience as closely as possible with standard Ruby. That, of course, is focusing more on the Ruby side, which is a fairly unusual development methodology for folks that are used to the JVM. Everything’s run at a command line. You generate a lot of code. You’re sitting at a REPL a lot of the time, doing most of your development and exploration that way. So definitely, compatibility and overall user experience would be the main guiding principles for JRuby. Then on top of that, we try to see once we have a solid, compatible Ruby implementation, what can the JVM bring to that, and how can it enhance that environment rather than overtake it or make it feel like Java again.

Daniel: Right. Gotcha. And so when people are writing, say, a gem that is used in JRuby, I guess generally they would be encouraged to not use Java interop, unless it was necessary, of course.

Charles: Right. We generally recommend that people build their gems in just pure Ruby, using other standard pure Ruby libraries. When it comes to utilizing the JVM, you can certainly call out from Ruby into standard JDK classes and into regular libraries pulled down from Maven or wherever else. We have kind of been a driving force in the Ruby community towards an emphasis on pure Ruby as much as possible. We don’t want to split the community. We want as much as possible for all gems to be pure Ruby and run on all implementations. Only when you need the specific features of a given runtime would you ever start calling into native code, or in our case, into Java code.

Daniel: Gotcha. I do want to come back to native code, but one question before we go deep down that rabbit hole is, tell me about your history with JRuby, and I guess maybe the overall history of the language for those who aren’t familiar.

Charles: So I started with JRuby back in about 2004. At the time, I had kind of reached the pinnacle of enterprise development. I was working as an enterprise architect for a large government contract. I mean, classic, classic sort of place to be. But I had a friend who did a little bit more wild and crazy JVM development and played around with languages. He suggested I check out this Ruby language. It’s something I’d never looked at or heard about in 2004. It turns out that the government contract I was working on was out in the Washington DC area, and RubyConf 2004 was going to be scheduled for one of my biweekly trips out to the home office. So at the time, it was about a $50 conference with about 60 people in it. I figured, why not? I’ll stick around for the weekend and go to the Ruby conference there. I was just struck with amazement because every talk, every piece of code that I saw, I understood immediately. It was the first time I’d ever approached a programming language and immediately been able to read it and understand what was going on. I thought, this is a really cool language, a really beautiful and fun language to use. I wonder if there’s some sort of JRuby project out there. It turned out that yes, there was a JRuby project that had been started a few years earlier in 2001 by a couple of folks. It was kind of dormant in 2004, not a whole lot of development being done. This was just at the beginning of the Ruby renaissance when it picked up Rails interest and got a lot of development. So I sent an email over to the JRuby project. It turned out that an old friend of mine from the University of Minnesota was the current main developer on the project. We basically just connected back up and have spent the last 20 years building JRuby on the JVM.

Daniel: Wow. Nice. And so you’ve been building JRuby, not just as a kind of nights and weekends, hobbyist developer, but for, I think most of that time, you’ve been employed by various companies to work, I guess, primarily on the language. I’m not exactly sure; I don’t see your day-to-day work, but my understanding was you’re kind of employed to drive JRuby forward. Is that roughly correct?

Charles: Yeah, actually, we’ve been very lucky as an open-source project, as a language implementation. In 2005 or 2006, folks at Sun Microsystems suddenly got very interested in having new languages on the JVM. This was right around the time that Rails was really popular. The Groovy language was getting a lot of interest, and the Grails framework was starting to come up. Just in our spare time from 2004 until the middle of 2006, we thought, what if we could make JRuby run all the same stuff that standard Ruby does? It’s an off-platform language. There are a lot of weird dependencies and libraries. So we worked nights and weekends, hanging out at coffee shops and hacking on JRuby code. In about the middle of 2006, we actually managed to boot up a Rails application and route the first early requests. I remember it distinctly. The first request was running really, really slow, and we were really worried that this was not going to work out, but we turned off development mode, switched into production mode, and suddenly it was totally usable. Now there was real potential for this thing. All those folks at Sun Microsystems who were excited about new languages on the JVM really just jumped on this. We did a presentation at JavaOne in 2006, and about six months later, they extended an offer to Thomas Enebo, Tom Enebo, the other lead of JRuby, and I joined the company exclusively to work on JRuby and interact with the JVM language ecosystem.

Daniel: Great. And my sense, you know, I’m not a JRuby user myself, but I’ve been following you, I think probably since I’ve been interested in Clojure. My sense is you have been one of the people on the cutting edge of that sort of JVM language ecosystem, particularly with Invoke Dynamic. Well, tell us about what Invoke Dynamic is and what your history with that is, because I think my memory at least was that you were quite either the reason for it or one of the main proponents of it.

Charles: So when we joined Sun Microsystems in 2006, there were early efforts to make the platform a better target for dynamic languages, as well as statically typed languages like Java. One of those efforts was Invoke Dynamic. It was sort of not really going anywhere. They weren’t really sure how to implement it or how to add support for dynamic languages at the JVM level. Once JRuby came on, we started having a lot of meetings with core Sun JDK developers at the time, folks who worked on the HotSpot VM. One in particular, John Rose, was an old Smalltalker, very excited about the possibility of expanding the JVM to dynamic languages. As someone who worked on the HotSpot runtime, the HotSpot JIT, he knew the potential was there for dynamic languages to optimize as well as statically typed languages. Members of our team worked with folks at Sun Microsystems and other Java contributors to finally spec out and build Invoke Dynamic.

Charles: So Invoke Dynamic is basically a new instruction, a new bytecode for the JVM, that allows you to essentially insert your own code into the JVM runtime at any point. So you do an Invoke Dynamic, it calls back into your runtime code, you say, “Okay, here’s how I go look up methods, here’s the actual function you want to call, now install this at that point.” From then on, the JVM treats it as though it was always a static call, even though you did all the wiring, you decided what the class structure was, and you decided how to make the call. That has been absolutely critical to making JRuby perform well on the JVM. We use Invoke Dynamic extensively. If you look at JVM bytecode that JRuby generates, it’s pretty much all just values moving around on the stack and Invoke Dynamic calls. The whole thing is very dynamic, and because the JVM understands that instruction, we’re able to get really good performance out of even purely dynamic calls.

Daniel: Right. Because Ruby is, not sure if “notorious” is quite the right word to use, but a very dynamic language where just about anything can be method lookup.

Charles: Oh yeah, it definitely makes us tear our hair out once in a while. Every time a new big version of Ruby rolls around, we kind of hold our breath to see what new dynamic feature they’ve decided to add in. So far, nothing they have done is impossible for us to implement on the JVM, even though sometimes it may be a little bit clunky.

Daniel: Right. So in the Invoke Dynamic callback, what’s the language that you’re writing when you get called back to wire things up?

Charles: So we emit an Invoke Dynamic into bytecode. That Invoke Dynamic has attached to it a method handle, the method handles API under Java Lang Invoke. The method handle is basically just a function pointer. So you say, “Here’s an Invoke Dynamic. Here is a bootstrap function pointer that will tell you what to do.” That call then goes into whatever function you’re pointing at, sets up a call site object, pulls method definitions from a dynamic class or creates a dynamic object, and then returns that call site to the JVM. The JVM treats that as though it was always there, as if it was always calling that dynamic function. All you have to do is write basically Java code to wire up these little function pointers in method handles.

Daniel: Nice. So JRuby itself, what’s the split of what you implemented in between Ruby and Java and perhaps other languages?

Charles: Well, it’s hard to say for JRuby itself. For the core JRuby runtime and core classes, the majority of it is written in Java code. We have a parser that is essentially ported from the CRuby implementation. We have our own compiler toolchain that dumps into our own interpreter, and then eventually a bytecode compiler that turns it into JVM bytecode. Most of the core classes, like strings, arrays, and so on, are implemented in Java, mostly out of simplicity for compatibility. If you look through our implementation of string and array, it’s going to look very similar to the C code for the same functions on the other implementation in the C implementation. That allows us to track changes a little bit more easily over time. We can just go line by line and see what behavioral changes they might have made. But we do have a growing core within the main JRuby runtime that is implemented in Ruby. We accept new feature patches in Ruby. We figure it’s probably going to perform well enough. If at some point we need to port it over to Java, we’ll consider that. But there’s no reason that we can’t implement more of JRuby in Ruby itself. The Ruby standard library, which is mostly pure Ruby, we share the exact same code with the standard implementation. All the same gems work, and all of the same standard libraries work with, you know, there’s a few C-based extensions that we don’t support, but they’re very rare.

Daniel: Right. What is the situation if there’s some gem that has a C extension, and someone on JRuby wants to use that specific library? The ability to write Java extensions, but how does someone, let’s say the Java extension exists, how does that get kind of used instead of the C one in JRuby?

Charles: If there’s a C extension that you have in an application and you’re thinking about moving to JRuby, the first step is just to see if there actually is a JRuby version of that extension already. We have a similar internal API for implementing extensions to JRuby. Most of the key libraries that are out there already have a, essentially a port to the JRuby extension API, and they will work out of the box. The gem will pull down the Java version of the library that will hook into JRuby internals, and it will function exactly the same way. Nothing would have to change in your dependencies or the way you use the library. The more difficult case is when there’s an extension for CRuby that doesn’t have a JRuby equivalent. Usually, we recommend three different ways to tackle that problem. The simplest would be to just find a pure Ruby version of it. If there’s a regular pure Ruby implementation of it, there’s a good chance that will perform well enough, and you won’t need to use the C version. The second would be to find a JVM library that’s essentially equivalent functionality and wrap it with a little bit of Ruby code. The third case would be that it might actually be worth it to port the original C extension to JRuby’s extension API and release it alongside the original gem.

Daniel: Gotcha. And so one gem can have both the C extension and the Java extension as part of the same gem package?

Charles: Yeah, part of the Ruby gems metadata is a platform marker. Normally, that platform marker is used for prebuilt native C extension gems. So rather than having to have a build tool, make, etc., on your system, you can release a Windows version of the gem, and it will pull the right version for your platform. We sort of overload that and have a Java platform as our platform for the gem. If it sees that you’re on JRuby and there is a Java platform version of that gem, it just pulls down a library with a jar file in it, and you’re all set.

Daniel: Nice. That is really clean. I like that. So what are some of the challenges you find writing a hosted language on the JVM? And I guess what’s your relationship now with this sort of Java team at Oracle?

Charles: Probably the biggest challenge that we’ve had implementing Ruby, beyond the fact that it is a pretty wildly dynamic language, is all of this native-level integration. There are a number of C extensions that over the years we’ve had to port or we’ve had to work with community members to build versions of. They change, they evolve. We’ve got to maintain those libraries over time. The core functionality in Ruby itself is often a very thin layer over standard POSIX APIs. If you look at Ruby’s IO class, it very much follows the standard IO operations that you would see in a C program. The creator of Ruby, Yukihiro Matsumoto, was a C programmer who wanted something better than Perl that he could use to write C code, essentially. So it has a lot of that POSIX history behind some of those APIs. That has meant we’ve needed to either emulate those behaviors on top of the JVM, which can be problematic, or we need to actually call down into the original native functions and provide a whole new parallel set of IO operations that JRuby can use.

Daniel: Right. If someone was using JRuby and they wanted to use some metrics library, you know, they didn’t know exactly what they needed to use, but there was a Ruby version and a Java version, which would people generally pick? Like the Ruby ones, I guess, are going to feel more natural to fit with the rest of the application?

Charles: If you’re a Ruby developer and you’re working in kind of a hybrid environment where you’ve got CRuby and you’re interested in starting to use JRuby, obviously the pure Ruby version, the standard Ruby library would be the best choice. We also have a ton of folks that come from a Java perspective and just want the language, want to be able to use Ruby on the JVM. In that case, it’s whatever fits best into your development process, into your deployment, into your ecosystem. It may make more sense for you to just use the Java libraries that are available.

Daniel: Nice. That’s a lot of flexibility, I guess, for the different places people come from into JRuby. I’m not sure if this is just my awareness, but it seems like there’s been a ton of new features and exciting things going on with the JVM since Java 9 onwards. I’ve never really understood why now. Oracle is not particularly known for its generosity in most places, and yet they seem to be funding Java very well in terms of the amount of output that comes out of the Java team. Do you know why Java has gotten so good in the last, I don’t know, five, six, eight years?

Charles: I think the biggest motivator for that was the move to open source. There are now so many research groups, schools, and companies that all want to try new things and play with new features for Java, the language, and the JVM platform. It’s provided an incredible test bed, probably the best-managed runtime that’s out there, fully open-sourced and available for people to play with. As far as actually getting all these new features into Java releases, as much as people like to knock the six-month release cycle that Oracle went to a few years back, that has really freed up engineers to kind of go wild with new features. There’s an LTS every so often; in between there, you can put preview features out and experimental features out. It’s not like the old Sun deployment, the Sun release cycle, where you basically had to work for two years, and if you missed your window, you had another two or three years to wait before you could get that feature in. It was very conservative in that process and very difficult for you to make a leap on a new feature when you don’t have any exposure for two years to actual users. The open-source side and that release cycle have made it much easier for existing JVM users to just play around with this stuff and help flesh out new features. It’s made it much easier to actually find the correct APIs and the correct design that will really be a useful feature when they put it into an LTS release.

Daniel: Right. And you’ve just published, only a couple of days ago, a blog post about JRuby and some of these next-generation JVM features and how they can be applied to JRuby. Do you want to take us through some of these and tell us what the feature is and how it’s going to help JRuby?

Charles: Sure. We have been kind of dragging our feet on upgrading the JVM. Like a lot of people, we have still supported Java 8, even in our current mainline JRuby development. Honestly, we still hear from people that don’t want to move off of Java 8. Those people are probably going to be out there for another 10 to 15 years. But we need to move forward with JRuby. So we’re now in the next major release, JRuby 10, making a leap to either Java 17 or Java 21. That’s opened up a lot of potential for us to try these new JVM features. Some of the stuff I have in the blog post, for example, we’re looking at a project called Project CRaC (Checkpoint Restore at Checkpoint). This is a feature that allows you to save a checkpoint in your process and then essentially freeze-dry the process at that point and launch it immediately when you start it up again. This solves a lot of startup problems for us. It’s using a feature called Checkpoint and Restore. Basically, you grab a checkpoint, and then you can restore from that checkpoint for future commands. It massively speeds up the boot time of the JVM because essentially, we’ve saved the entire booted JVM as an image.

Charles: The other big ones that we’re looking at are Project Leyden, which is a similar effort to improve startup time and warm-up time. In that case, it saves off the booted classes and some of the JITted code, and then you still start a brand new JVM, but it already has a bunch of work from previous runs available. That will probably be a little bit more portable than the CRaC project, which requires Linux right now. I did mention we also have the issues integrating with native code. Of course, Project Panama is a huge part of that in the future. Panama brings a foreign function interface so we can call native C code, a foreign memory interface so we can allocate and manage memory, and some code generation to the JVM with JIT help to make those calls fast and make integrating with native code just as fast as calling into any JVM library.

Daniel: Nice. Would that let you use the Ruby C extension API, or is that kind of like crossing the streams too much to call directly into?

Charles: Well, a frequent use of Ruby C extensions is just to provide a Ruby wrapper around some C library. For example, one of the most popular C extensions is called Nokogiri. It is a wrapper around libxml that provides XML support in Ruby rather than trying to build an entire XML stack in pure Ruby. They use a trusted library. That is the sort of library that we could wrap using Panama and provide the same functionality for Ruby users on the JVM, or indeed for Java users that just want to use these libraries. We point it at a header file, we generate some JVM code for Project Panama, and then we can call that C library as if it were any other JVM library. So it’s usually those cases where it’s a specific functionality that only exists in a native library, a C library, that we want to be able to use from Ruby. Now we can use Panama to make that a lot more scalable.

Daniel: Nice. Going back to CRaC and Leyden, how do these deal with things that shouldn’t be snapshotted? I guess secure random state was the main one that came to mind. What happens there?

Charles: The CRaC API is still evolving at this point. We are trying to play with it to push it forward. The way that you deal with resources like that, secure random, for example, file descriptors, locks that have been acquired, and other resources, operating system-level resources that are really tied to the process itself, you can specify a pre- and post-checkpoint cleanup fix-up code, basically. So it captures a checkpoint. It’ll call back into your code and say, “Okay, I’m about to capture this checkpoint. Do whatever you need to freeze everything.” After restore, it calls a different callback. You can reconnect to sockets and databases. You can re-seed your secure random—all the things that you would need to do in a new process, but we’ve essentially skipped all the other boot time stuff. It just lets you only boot what really needs to be fresh in that new process.

Daniel: Nice. Another one which has been out for a couple of JVM releases now is Project Loom. So tell us about Project Loom and I guess why it’s particularly valuable for JRuby compatibility with Ruby.

Charles: Project Loom is one of the most exciting recent projects for us. Loom brings to the JVM the concept of virtual threading. So it looks like a thread, you launch it like a thread, it runs alongside other threads, but it can potentially be rescheduled across different native threads. You can have hundreds or thousands of virtual threads that say they make a blocking I/O call or they wait on a lock. Well, they can be descheduled, and a different virtual thread can run for a while. In JRuby, that has solved a huge problem for us, which was implementing Ruby’s concept of a fiber. A fiber is basically a coroutine. You start it up, you can iteratively pull values from it, you resume the fiber, it does some computation, it yields a value back, but it is essentially like a little virtual thread that continues to feed you values and continues to have its own call stack and run off to the side. Before Project Loom, JRuby had to implement fibers the only way we could, using another native thread for every single fiber. That is obviously a huge amount of resource usage. Most systems are not going to let you scale beyond a couple thousand native threads, and it was quite a bit slower than being able to do a direct handoff. These threads all have to coordinate; the CPU has to decide to schedule them, and it ended up being a real bottleneck, both in terms of resources and performance for JRuby. We have integrated Project Loom, I think about two years ago on one of the early versions even, but if you run JRuby on a Loom-enabled JVM now, every fiber is actually backed with a Loom virtual thread, and we can scale to tens of thousands, hundreds of thousands of fibers very easily now. It’s really impressive work on the Loom side, and it required almost no changes in JRuby to make it go.

Daniel: That’s nice. Tell us about Project Babylon.

Charles: Babylon’s one that I found out about just recently at the JVM Language Summit in early August. The attempt with Babylon is to basically provide a new model for code, for executable code on the JVM. Rather than everything becoming bytecode and it’s just always bytecode, which loses a lot of context and code structure, instead, what we can get is compile it to what they call a code model in Babylon. This is going to look sort of like a standard computer science 101 compiler graph. It’s going to represent things as blocks with jumps and instructions, a little bit more structure than what you get out of JVM bytecode. What you can do with that structure then is take code that would normally only end up as JVM bytecode, and you can translate it. You can recompile it for, for example, a SQL database, for a GPU, for a CUDA or OpenCL set of instructions. Just by writing Java code and feeding it through a Babylon code model, you can potentially target a lot of exotic backends because rather than just representing all that code as flat JVM bytecode, we’re representing more the computation aspect of that code and can translate it very easily.

Daniel: Next one here is Project Valhalla. This one has been a very long-running project effort. Tell us about Valhalla and why it’s useful for JRuby.

Charles: Valhalla is a big one. Ever since generics came to the JVM, people have been screaming about why aren’t they reified? Reification—what does that mean? Basically, we have this split between object references and primitive types on the JVM. Java developers will be well familiar with the fact that you can’t do an ArrayList of int. There’s no way to represent that. You have to do an ArrayList of Integer because generics only operate over object references and not primitives. For years, folks have been wanting to be able to do things like a list of int or a map from long to float and have it be efficiently represented and indeed even be able to do it at all. That’s generic reification, a huge challenge to implement at the JVM level because the concept, the split between object references and primitives is so ingrained into the bytecode, the JVM, the JIT—all of that. The entire runtime needs to change to support it. What they have smartly decided to do in Valhalla is kind of split that effort up. The recent work that is available in preview form provides support for value types, which are sort of a halfway point between primitives and objects. They are on the stack like a primitive value would be, like an int or a float, but they can behave like an object reference. No object has to be allocated for it. The JVM knows that this representation is basically a flat stack-allocated version of this value. We’re hoping to use that in JRuby for some of our numeric boxes. We have a lot of fixnum objects and float objects floating around in a typical JRuby runtime that have a cost. There’s an allocation cost, there’s a GC management cost. If they can be value types, ideally that cost goes away, and they’re no more expensive than primitives. On the other side, as they feel out how value types propagate through the JVM, what work is required to support them, that will obviously feed directly into reification. If we can represent primitive values in something that looks like an object, then it should be easier for us to start creating a list of int, creating a list of these value types that will represent primitives compactly without object wrappers and still feel like you’re dealing with all objects. It’s a huge project, but there’s a lot of potential for JRuby and pretty much every other JVM language to be able to bridge that gap between objects and primitives.

Daniel: Do you know what kind of weird project Valhalla is and what its current status is? It looks like some things are already out, but still a way to go.

Charles: Yeah, they’re asking for folks right now to play with the value types, early access stuff that they have. They did some interesting examples at the JVM Language Summit. I believe those videos are online as well. It’s functional at this point. What they are working on now are the weird edges, edges like what does null mean for a value type? There is no null in a primitive. So how do we represent a value type that is basically a primitive floating around a system that allows for nulls to show up? Maybe we don’t allow nulls. Maybe we have a specific non-nullable type marker that goes along with this in the language, something Java developers have been wanting for years, would actually solve some of those complications. On the other side, the reification, I think there’s still a lot of work to be done. There have been preview versions of every attempt they’ve made at it, and they are currently on the third or fourth iteration of how to implement reification. I’m not sure how solid that is yet, but they are interested in having people play with the existing builds as well.

Daniel: Nice. I think Clojure has some similar challenges to JRuby in that respect with primitive and object types. I suspect or hope that that improves things for Clojure as well.

Charles: Yeah, I’m sure it’ll have some great impact on numeric manipulation and numeric types within Clojure as well, especially aggregate types. They talk about things like complex and rational types that you really don’t want to have a full object box around all the time. That’s where all languages on the JVM would be able to benefit from value types.

Daniel: Right. Yeah. Let’s talk about garbage collection in JRuby. There’s now a wealth of riches of garbage collectors available in Java. Is there anything particularly special about garbage collection in JRuby from the perspective of which garbage collector you would pick or how you might tune it?

Charles: Not really. We usually just recommend that people run with the standard G1 collector that’s default on most JDK builds these days. But all of the garbage collectors work well. The reasons you would pick one over another for a large Java application pretty much apply to JRuby as well. If you’ve got terabytes of data in memory, you might want to use a different collector. If you’re doing a small, short-lived application like a command-line program, maybe you want to use a simpler collector rather than G1. JRuby and Ruby objects within the runtime are just Java objects. We benefit from all the same GC technology, from the same heap dumps, and the same memory profiling tools all work with JRuby. The only place where it sometimes can get a little weird is when we are integrating with native libraries. Like any language on the JVM, if you’re starting to do your own management of off-heap memory, you’re kind of on your own. The same thing applies with JRuby if you’re using our foreign function interface, our FFI, to call native libraries, or if you’re using a Ruby library that has a large amount of native data associated with it.

Daniel: Right. So you’ve got JRuby 10 coming out soon, sometime this year, I think? Is that the plan?

Charles: We could probably release it at any point. We have pretty much parity with Ruby 3.4, but I think it would be polite for Ruby 3.4 to release first. [laughter] You know, we’d love to jump the gun and say, “We’re the first Ruby 3.4 implementation,” but we have a good relationship with them. We don’t want to do that. They always release on Christmas. Every year is a new Ruby release, and so probably shortly after the new year, we will look at putting out our JRuby 10 release.

Daniel: Okay. Do you contribute much to CRuby itself, or that kind of side of the Ruby development ecosystem?

Charles: I contribute a fair amount to the pure Ruby parts, certain pieces of the standard library, helping gem authors build libraries that are thread-safe, that will work well across implementations, especially on JRuby, helping with some new feature discussions, making sure that they’re not going down a weird road that’s going to be difficult for runtimes to implement. My association with core Ruby development is kind of in an advisory capacity. Knowing what I know about building Ruby on the JVM, here are things to look out for, here are threading issues that we need to fix, trying to make sure that they don’t make decisions for Ruby that would prevent other implementations from fully supporting the language.

Daniel: Great. It sounds like they’re, I guess, sort of open to considering not just CRuby, but the wider Ruby ecosystem of other implementations and how their actions would affect those implementations.

Charles: Yeah. Luckily, Ruby core has been pretty receptive to JRuby over the years. At first, it was a weird oddity, like, what is this Java thing that’s coming in here and trying to do Ruby stuff, especially back in the mid-2000s, when a lot of Rubyists were ex-Java people that had just escaped from that enterprise world. They didn’t want to see anything relating to Java. It was an interesting relationship for a while. But the Ruby core folks in general and even the community at large have been fairly receptive. The fact that we focus so heavily on compatibility and keeping the experience the same, I think, has made it easier for people to approach us. They know that JRuby can run the same things that they do in their daily Ruby development, but they love the potential of the runtime and all of the libraries that are available.

Daniel: Nice. Tell us a little bit about what’s going to be happening recently with JRuby, because it seems like it’s going through a new phase, a new lifecycle of the project and of how you’re working on it.

Charles: There are definitely some big changes happening. Since we were first hired on by Sun Microsystems in 2006, JRuby has been continuously funded with at least two full-time developers for that entire time. At first, Sun Microsystems for about three years. Then around the time of the Oracle changeover, we decided to make a move to Engine Yard, which was a very Ruby-focused hosting cloud provider. They sponsored our work for another three years while we helped get JRuby into their Ruby cloud. Then after Engine Yard in about 2012, we made the switch to Red Hat. Red Hat, for 12 years, basically just funded us to work entirely on JRuby as a project. We worked with some of the middleware app server team members on app server support for JRuby and for Ruby applications. We worked with some of the JVM JDK team there over time. They funded us going out and talking about the work we were doing and reaching out to the JVM language community. But all good things come to an end. In about May of this year, we found out that after 12 years, Red Hat was going to end their funding of JRuby development. It’s hard to feel too bad about 12 years of open-source funding for all that time. Of course, we would have loved 20 years, but we understood that there was going to be an end to that whole relationship. As of July 4th, I’ve been taking JRuby on as an independent project. I am working with sponsors from the Ruby and JVM communities to continue to fund my work. We’ve also spun up Headius Enterprises, my company, to provide commercial support services for all of the hundreds, thousands of JRuby users that are out there. We’ve gotten a pretty good response. It’s only about two months now, and we’re really kind of just still working through the first list of customers to get them on board. The future of JRuby development is not in question. I love working on the project. I love being part of the community. I will work on it regardless. But with sponsorship and with support contracts, it means that I can dedicate full time to the project and make sure that it keeps moving forward at the same rate.

Daniel: That’s great news. So tell us more about what this commercial offering looks like.

Charles: Right now, we have a low-end tier, a sort of pro tier, that essentially is about a hundred bucks a month. It will get you a direct line to me and some other JRuby developers if you have specific questions or issues that you need to deal with. So it’s kind of an entry-level offering. What we are going to be launching very soon is a middle expert tier that provides things like a 24-business-hour response time on bugs that relate to your application, guarantees on security fixes or security mitigation, a few dedicated hours per month that you can call on me and ideally some of the other JRuby developers to do specific fixes and features that would affect your application, and things like profiling and monitoring help for closed-source applications. Your commercial app that you’ve built on JRuby, we can help with the entire lifecycle to make sure that it continues to scale and that any issues you run into are dealt with quickly.

Daniel: Nice. That seems like a very reasonable, certainly reasonable starting point for any company that uses JRuby. It seems like it would be in their interest to support you, at least one of those tiers, to ensure the health of their own infrastructure.

Charles: Definitely. We’ve done a lot of this work. We’ve essentially had these relationships for years, but Red Hat was basically footing the bill for us to fix user-specific or application-specific issues and help people find performance and scaling problems. Now the opportunity is getting these companies on board, providing a good value for them to make sure that they’re not running into issues they can’t deal with, and ideally that they don’t have to hire on another resource to be their JRuby expert. They can call on Headius Enterprises to be that expert and have their developers focus on building functionality.

Daniel: Awesome. So tell us more about JRuby 10 and what’s going to be involved in that release.

Charles: Yeah, I mentioned JRuby 10 just in passing. We are currently maintaining our JRuby 9.4 line. That is, as I mentioned, supports Java 8. It is based on Ruby 3.1 compatibility, which is now a couple of years old. JRuby 10 is going to be a big leap toward all the newest stuff. It’s going to support Ruby 3.4, which is actually an unreleased version of Ruby coming out later this year. We’re keeping track of compatibility there and should be on schedule to release shortly after they do. So that’ll jump us right to the top of compatibility with standard Ruby. Also, the big JVM move, moving up to Java 17 or 21, so we can leverage more features that have been added to Java and the JVM and pass that on to Ruby users. Most exciting for me personally, since I do a lot of the compiler and optimization work, is the fact that we can actually start looking at optimizations we haven’t done for years. Once we’ve finally caught up with compatibility with CRuby, that extra time we would spend on compatibility can now go towards optimization. There are tons of little things and major optimization changes that I’ve wanted to do for years, but compatibility came first. I’m excited to be able to take a step back from day-to-day compatibility work and really bring more of the performance potential of the JVM to Ruby users around the world. We’re looking at a release shortly after the new year, but it’s a functional stable branch right now. We’d encourage anybody to give it a shot if they want to see what next-generation JRuby looks like.

Daniel: Great. And we’ve kind of touched on this through the podcast, but in terms of performance, what is the kind of performance delta between JRuby and CRuby, both in terms of startup time and sort of throughput in a running application?

Charles: Well, I’ll start with the good news first. JRuby generally will perform better than the standard C implementation. We have better, more mature JIT technology. We have much more robust garbage collection and memory management. We usually expect that if there’s not something else slowing it down, something artificial like excessive allocation or bottlenecking on mutex, that a JRuby application will run Ruby faster than the standard implementation. What does faster mean? That can be anywhere from slightly faster to three to five times faster. In some cases, we can get into double-digit multiples of CRuby performance simply because we can leverage the capabilities of the JVM better. The bad news is that like a lot of JVM applications and libraries, startup time is not great. And by not great, I would say it’s like a hundred times slower to startup than CRuby. CRuby is designed to start quickly. You will get a hello world coming up in just a few milliseconds. There are very few hello worlds running on the JVM that can even approach that. Add to it all of the JRuby boot time, all of the internal class structures that we need to set up for Ruby’s core classes, and you’re looking at more like a second of time or a second and a half to get the application to boot. That’s the big reason we’re excited about things like Project CRaC and Project Leyden, that will finally sweep away some of that startup boot time and get the command line experience of JRuby much closer to what Ruby developers depend on.

Daniel: Nice. Would you say that’s like one of the last major differences between CRuby and JRuby?

Charles: Over the years, I would say that the number one reason, and maybe the number two and number three reasons that people don’t use JRuby instead of the C implementation is the startup time issue. Ruby development, Rails development is heavily command-line driven. You are running a lot of generation commands at the command line. You are starting up a REPL. You are spinning up servers, bringing them up and down. Having any sort of delay there really just damages the flow of the whole process. If we could get JRuby command-line operations down under a half a second, I think one of the biggest reasons that people have turned away from the project would essentially just disappear. I’m really excited to see what people will do with JRuby once we can get down to a hundred millisecond startup time, which looks like it’s possible with projects like CRaC.

Daniel: That’s very exciting. Clojure, as you can imagine, has very similar challenges with startup time. So looking forward to seeing the fruits of that work be applied to Clojure too. All right. Is there anyone you’d like to thank or mention in relation to your JRuby work?

Charles: There are a number of sponsors that have really stepped up. A couple dozen small sponsors that, of course, I’m very happy that they’re contributing anything that they can. I’ll have links for the podcast that anybody else who’s interested can jump on. But specifically, Shopify has stepped in as a big sponsor. For a short period of time, they are helping to fund JRuby development through a GitHub sponsorship. We’re very thankful for that help, helping with this transition time, helping us get JRuby support up and going and find other sponsors for the work. In addition, there is a friend of ours from RubyConf India, Gautam Regge. He is one of the co-founders of Josh Software, which does a lot of Ruby development in the Pune and Bangalore areas. We have some other users of JRuby that have jumped in. The BSCM community is a friend of ours from Thailand. They build a JRuby-based hotel management suite that they sell as packaged software to hotels in Thailand and the surrounding area. They’ve jumped in as a big sponsor. A fellow I talked to recently, Mark Triggs, who does contracting work using JRuby and wanted to see the project continue to succeed, also jumped in at a fairly high level of sponsorship. So every little bit helps, but I’m happy to give shout-outs and promotion to anybody who jumps into some of the higher sponsorship tiers.

Daniel: Yeah, that’s great to hear. So people can either, I guess, sponsor JRuby out of the goodness of their heart, but you also have a commercial offering, which in my experience, sometimes is an easier sell to companies where they can see like an invoice with an exchange of goods and services for money in it.

Charles: Yeah, absolutely. If you go to Headius.com, it’s going to be our new company. We will have things like contracting services. We’ll be working on other technologies as well. But the main product right now is the JRuby commercial support offerings. We have a whole range of features that we are working to deliver to commercial users of JRuby. It’s been a great response so far, really just still kind of ramping up and seeing what the needs are from the community. But if anybody out there has a JRuby application that they are building, something they’re running in production, or maybe a packaged piece of software that they sell, we can help fit into every scale of JRuby application and make sure that you don’t have any surprises that impact your customers or your users.

Daniel: Excellent. Well, thank you very much for this conversation and for contributing to the Java ecosystem and the Ruby ecosystem. I think your work has impacted a lot of Clojure developers indirectly. So thank you for that and for what you’ve done for the wider open-source community.

Charles: It’s been a great adventure working with the JVM language community. It’s not something I ever expected to do, but it’s been very rewarding. Here’s hoping that I can spend another 20 years working on JVM languages because it’s been a lot of fun.

Daniel: Yeah. Best wishes to that. Thanks a lot.