JRebel 3.0 Release - Hotswap Reloaded
With the recent release of JRebel 3.0 we spoke with Zeroturnaround's CTO Jevgeni Kabanov to detail some of the internal workings of JRebel, applicability and integration issues and understand the benefits a Java developer could gain from this technology.
He recently published a series detailing JVM classloading and runtime class replacement. The information in this article is partly distilled from the series and partly taken from our communication.
The need to redeploy or restart a Java application during development introduces unecessary delays which reduce productivity. JRebel aims to reduce development turnaround time significantly by adding a specialized javaagent to the JVM's startup parameters that handles the more difficult details of runtime class replacement.
Turnaround and Productivity
To support the discussion of productivity, Zeroturnaround gathered statistical data from over 1100 Java developers. They made the data publicly available and used it to calculate average hourly waiting time of of 6 minutes for building and about 10.5 minutes for redeploying application
Besides the direct costs of the time spent waiting, there are other, hidden costs. Context switching in a complex task environment like software development is a major problem. The amount of information that has to be kept in short term memory for development flow is quite high, but it deteriorates quickly when focus is lost. Recovering the original context also takes a substantial amount of time. In the available literature the cost of context switching is discussed for workplace interruptions like phone calls, emails and personal requests. But it also applies to the infrastructure induced causes shown earlier.
The reduced productivity can contributes to a lower developer motivation due to the lower number of successes and achievements during the workday.
There are several ways to address the problem of updating changed classes within an application without restarting, but all come with different problems.
One might argue that JVM Hotswapping which replaces bytecode in place, has been around for some time (since 2002, JDK 1.4). It has serious limitations, i.e. it only works in Debug mode and only handles changes within method bodies. It doesn't allow structural changes or adding or removal of class members. Hotswap is also ignorant of classloaders, it identifies classes only by their name and works only for existing classes.
Since Java 5, hotswapping is available through the instrumentation API.The API is also used by JRebel, but only used to instrument infrastructure class loaders and classes. It does not contribute to the actual reloading process.
The complexity of the JVM - especially garbage collectors, memory layout and Hotspot make it difficult to provide a general solution for transparent class bytecode replacement.
One problem is that the state of object instances must not be affected. Otherwise the concerned instances would have to be replaced completely and all references to them updated as well (including cascading changes)
Application Server Redeployment/Hotdeployment
The redeployment features of most servlet containers, application servers and OSGi implementations each work along the same lines.
You load an application (or part of it) with a dedicated classloader. If you remove all references to the loaded classes as well as the classloader you can unload the application and reload it completely using a new classloader. Often it is not possible to remove all references to instances, classes or classloaders completely from the JVM.
Then the old application is partly still around, it is just not longer used. So classloader leaks are introduced that lead to increased memory consumption after continuous redeploys.
Hotdeployment is effectively a restart of the application. To restore the application state it must be stored (e.g. serialized) before and then reapplied or reloaded into the "new" application or module.
Current component-based frameworks like Grails or RIFE are responsible for handling component state themselves. That's why reloaded components with new classloaders are not an issue for them. The state for the new component is already available within the framework. The component granularity allows also very small bundles to be reloaded making the experience almost instantaneous.
A general problem with reloading classes this way is, that the old and new class versions are available at the same time and have to be carefully handled by the framework.
JRebel, which has been available since 2007 and was formerly called JavaRebel, takes a different approach. When a class is loaded by an instrumented class loader, an indirection is created on the fly, which enables the reloading while preserving identity and state of all instances. The indirection, which is based on "advanced compilation techniques (like abstract bytecode)" results in one master class and several anonymous, JIT enabled support classes. JRebel tries to leave method invocations as much intact as possible to have minimal impact on performance. For the same reason it avoids instrumenting the JDK. To support the reflection API for reloaded classes, results of the API calls are modified accordingly.
The creation of additional classes results in a 20 to 40 percent higher perm gen space allocation. According to Jevgeni, smaller classes and deep inheritance hierarchies are more probable to use more memory. But this is just due to the internal implementation, you should not rely on that.
For reloading classes in a packaged deployment JRebel allows the developer to map (via rebel.xml configuration) the packaged file structure back to the development workplace to enable reloading of changed classes and all other resources.
The equal consideration of resources for reload/refresh also enables updating of configuration and metadata information of various frameworks and containers. To accommodate the correct refresh facilities for the different frameworks, an open source API is provided to develop JRebel plugins. There are open source plugin implementations available for Guice, Spring, Tapestry 4, Struts 2, Wicked, Stripes, WebObjects. Those are partially included in the release distribution.
The differences between the three class reloading approaches are visualized in this Comparison Matrix.
JRebel 3.0 improvements
Release 3.0 of JRebel which was released April 16th contains a number of important improvements.
- performance improvements, twice as fast startup time and 25%-30% less memory usage
- Support for adding static fields and changing enums (automatic intialization)
- improved EJB Support and plugins, especially for IBM WebSphere, JBoss, Oracle Weblogic - support EJB interface changes, dependency injection, more EJB 3 support to follow
- JPA - OpenJPA and Hibernate plugins that reflects changes to entities, annotations and configuration (beta, disabled by default)
- full JSP scriptlet support, outside code changes immediately available in scriptlets
- Mojarra plugin for JSF configuration and annotation changes
- more plugins available for Spring , Guice, Seam, Weld Groovy, Wicket, Google App Engine, AspectJ, Struts 1 and 2
- special support for bytecode frameworks like Javassist and cglib which are used by many other tools
According to Jevgeni the last issue was a tricky one. When adding methods to classes the invocation handlers of the bytecode frameworks received synthetic method calls that they didn't expect. So the JRebel API was extended with a
redefineClass() method that also allows the redefinition of bytecode proxies. Using the API it became then possible to rewrite parts of the proxy implementation to allow for dynamic class changes. Now there are two plugins supporting especially those frameworks.
One of the remaining issues are JDK dynamic proxies which need a lot less integration due to their limitation to proxying interfaces.
The goal for JRebel is to get more and more compatible to allow for a seamless integration with any Java based infrastructure. One important step there is the improved EJB integration.
For better enterprise integration Zeroturnaround added the JRebel Enterprise Add-On which targets older technologies (Java 1.4, older application server versions, EJB 1.x and 2.x) and centralized license management. It is supplied as a free update for all current users.
Licensing and Support
I'm curious what "more EJB 3 support to follow" exactly is. Happy anyway that such important Java technology as EJB is getting better support. Looking forward to better JDK proxy support also.
Thanks for this wonderful piece of software!