Channels ▼
RSS

Parallel

Language of the Month: JRuby


In December, JRuby — a language that brings Ruby's easy syntax to the JVM — turned ten years old. Over the course of those years, numerous companies have embraced it as their primary language implementation of choice. From the refueling systems at the Oslo airport in Norway, to the industrial laundries at some of the biggest hospitals in Minnesota, to software consultancies and Internet companies such as LinkedIn, JRuby delivers products for regular use.


This article doesn't aim to be an introduction to the Ruby world. Instead, I want to explain why the marriage of an expressive language like Ruby and the JVM is so convenient, and how developers can take advantage of it. 



Where JRuby Improves on Ruby

When Ruby was ported to the JVM, its developers needed to make a controversial decision: whether to mimic the primary Ruby implementation piece by piece, or adapt the language to the platform, and thus make the language a "better" Ruby. They chose the latter.


The most notable adaptation was the introduction of a compiler. Other Ruby implementations run the code directly, executing the generated abstract syntax trees (AST) as the program is running. Although this approach works fine most of the time, it's not the most optimized way to run an application. Every time the program steps through the AST, it has to make decisions and perform the same actions repeatedly.


JRuby introduced a compiler with two strategies: ahead-of-time compilation (AOT) and just-in-time compilation (JIT). The first strategy compiles the Ruby code to Java bytecode as would happen with any a program written in a compiled language like Java or C. The second strategy compiles the code while the program is running. The JIT optimizes performance over time, especially when the same code is called frequently.


Another adaptation is the approach to threading. The primary Ruby implementation uses what are called "green threads" in its 1.8 branch; they are just swapped around every 10ms or so. The 1.9 branch uses real native threads, but the interpreter uses a Global Interpreter Lock (GIL) to keep them from running concurrently. In its early days, JRuby removed those limitations. Every thread is treated as an independent runner by the JVM without any lock between the interpreter and the operative system. This adaptation allows JRuby to take full advantage of multicore systems.


Consistent with these design choices, JRuby has been carefully adapted to run on the JVM. It provides several options to tune its performance — from enabling experimental features, such as Java 7's invokedynamic support, to modifying the behavior of the JVM internals. The same flags that we can use to modify the memory allocation or the behavior of the garbage collector for Java can be used to modify the same parameters for JRuby.


Deep Integrating Between Java and Ruby

JRuby integrates both Java and Ruby, allowing them to interact with their original platforms. It takes the Java language and adapts it with Ruby idioms, but also allows it to run Ruby programs inside a scriptlet container from Java code. In this section though, I examine on how Java can be integrated with existing Ruby code.



ruby
require 'java'


Everything starts with the simple command above. Before calling that instruction, JRuby communicates only with the Ruby world. The line of code loads the Java interoperability classes required to integrate both languages. The Ruby Kernel module is overloaded with new methods that will help us to work with Java classes.


With this code, we can instantiate any Java class using its fully qualified name (package and class name) and its constructor. As the example below shows, we use the same idiom that we already used to create an instance of a Ruby class.



ruby
list = java.util.ArrayList.new


If we want to create several instances of the same class, we don't always need to use the full name of the class. JRuby adds two convenience methods to the Kernel to simplify this task and allow us to use just the class name. We can use java_import to include a Java class; after that, we no longer need to use the full name:



ruby
java_import 'java.util.ArrayList'
list = ArrayList.new

JRuby also provides a method called include_package, which we can use when we want to load any class that belongs to a Java package. It works only in Ruby modules, but it will help us to define common and less complex namespaces:



ruby
module Util
include_package 'java.util'
end

list = Util::ArrayList.new

There are other convenience additions beyond constructors and loading methods. For instance, the getters and setters into our Java classes are also aliased as Ruby attributes:



java
public class Person {
   private String name;
   public String getName() {
       return name;
   }
   public void setName(String name) {
       this.name = name;
   }
}


ruby
person = Java::Person.new
person.name = 'John'
puts person.name

The integration is so deep that we can use Ruby metaprogramming techniques with any Java object. One fundamental aspect of Ruby is the concept of "open classes." This means that we can redefine any class at any moment in our code and add and remove behaviors. Java classes also allow this when run from JRuby. In this example, I show how to add the method map to a Java ArrayList:



ruby
java_import 'java.util.ArrayList'

class ArrayList
 def map
   new_array = []
   it = iterator
   while it.has_next?
     new_array << yield(it.next)
   end
   new_array
 end
end

al = ArrayList.new([1, 2, 3])
new_al = al.map do |el|
 el + 1
end

Fortunately, we don't need to add Map to ArrayList because JRuby already includes the module Enumerable between the ancestors of the ArrayList, and it brings the method map along others to this class, as show here:


jruby-1.6.5 :001 > java.util.ArrayList.ancestors

=> [Java::JavaUtil::ArrayList, Java::JavaUtil::RandomAccess, Java::JavaLang::Cloneable, Java::JavaIo::Serializable, Java::JavaUtil::AbstractList, Java::JavaUtil::List, Java::JavaUtil::AbstractCollection, Java::JavaUtil::Collection, Java::JavaLang::Iterable, Enumerable, Java::JavaLang::Object, ConcreteJavaProxy, JavaProxy, JavaProxyMethods, Object, Kernel]

As you can see with these examples, even when we're working with Java classes, the code is fairly similar to Ruby code. I've only just scratched the surface of the Java interoperability. With this capability, JRuby opens the door to integration with other JVM languages. Scala, Clojure and any other language that compile to Java bytecode can be used with JRuby.



Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video