image blog class loader slow down
January 14, 2017

Reload Java Classes at Runtime With JRebel

Developer Productivity
Java Application Development

In this post, we look at what happens when we reload Java classes during Java application development. We discuss how much time is wasted rebuilding the project and restarting the application server every time you want to see the code change reflected in the running app, which is all the time. Learn how Java loads the classes and why classloaders hierarchy means it's not easy to keep them from wasting time on rebuilding and redeploying your app.

Back to top

The Java Application Development Process

When dealing with Java application development, you go through several actions over and over again. The same is mostly true for any technology stack. To implement a change in the app, you first need to code it up, then run the application and verify that the changes you just implemented are truly what you wanted them to be -- functionally correct and working. The problem is that the latter can take quite some time, especially with the Java stack. You typically need to build your project, either fully or partially, tear down and restart the Java process running your code and load or deploy your application again. diagram of build/deploy/test/change code time sucking circle of hell

Two of these phases are irrelevant to implementing the changes. Building, packaging and deploying is not necessary -- rather an artifact of the platform than a necessity. It would be much faster and easier if we could just reload the classes in the JVM with the new class definitions. This way, when the JVM accesses the classes or objects of the changed classes next time, new code would already be in there. It would save a lot of development time currently wasted.

Get a glimpse of emerging trends, changes, and more in the Java community. Get the 2022 Java Developer Productivity Report

Download the report

Back to top

Time-Consuming Java Class Reloads

Let's talk about how Java loads your application classes to execute its code. Once a Java class has been loaded by a class loader, it’s immutable and will last as long as the class loader itself. The identity is the class name and class loader identity, so to reload an application, you’ll need to create a new class loader which in turn will load the latest version of the app classes. You can’t map an existing object onto a new class, so it’s important to migrate state across around reloads. It could mean recreating the whole application object graph by reinitializing the application and config state etc. or copying over user session state. Very often this is also time-consuming and is very vulnerable to memory leaks.

When dealing with memory leaks with class loaders, a small one line leak can be amplified by the reference model Java uses. For instance, a class loader instance will have a reference to all classes it loads and all instances of the objects that are then created. So even a small leak, perhaps added in state migration between application instances during a reload, can, in fact, have much larger repercussions.

So, what does this mean for you as a developer? It means constant compilation, build, packaging, re-deployment, and application server restarts that just get in the way of your focus and your interesting, productive work that you want to get on with.

Back to top

What Is a Class Loader?

A class loader is an abstract Java class, ClassLoader, which can be implemented by a class you create.

A class loader is just a plain java object.Yes, it's nothing clever, well other than the system class loader in the JVM, a class loader is just a java object! Here is the API:


public abstract class ClassLoader {
  public Class loadClass(String name);
  protected Class defineClass(byte[] b);
  public URL getResource(String name);
  public Enumeration getResources(String name);
  public ClassLoader getParent();
}


Looks pretty straightforward, right? Let's take a look method by method. The central method is loadClass which just takes a String class name and returns you the actual Class object. This is the method which if you've used class loaders before is probably the most familiar as it's the most used in day to day coding. defineClass is a final method in the JVM that takes a byte array from a file or a location on the network and produces the same outcome, a Class object. A class loader can also find resources from a classpath. It works in a similar way to the loadClass method. There are a couple of methods, getResource and getResources, which return a URL or an Enumeration of URLs which point to the resource which represents the name passed as input to the method.

Every class loader has a parent; getParent returns the class loader's parent, which is not Java inheritance related, rather a linked list style connection. We will look into this in a little more depth later on. Class loaders are lazy, so classes are only ever loaded when they are requested at runtime.

Classes are loaded by the resource which invokes the class, so a class, at runtime, could be loaded by multiple class loaders depending on where they are referenced from and which class loader loaded the classes which referen... oops, I’ve gone cross-eyed! Let's look at some code.


public class A {
  public void doSmth() {
    B b = new B();
    b.doSmthElse();
  }
}


Here we have class A calling the constructor of class B within the doSmth method. Under the covers this is what is happening


A.class.getClassLoader().loadClass("B");


The class loader which originally loaded class A is invoked to load the class B.

Class Loaders are Hierarchical, But Like Children, They Don't Always Ask their Parents

Every class loader has a parent class loader. When a class loader is asked for a class, it will typically go straight to the parent class loader first calling loadClass which may, in turn, ask it's parent and so on. If two class loaders with the same parent are asked to load the same class, it would only be done once, by the parent. It gets very troublesome when two class loaders load the same class separately, as this can cause problems which we'll look at later.

When Java EE application servers implement the Java EE spec, some prefer to delegate to the parent always, but others choose to look locally within the Web App class loader first. Let's go into this in more depth and use figure 1 as our example.

image blog jrebel reload java classes at runtime

Figure 1

In this example, Module WAR1 has its own class loader and prefers to load classes itself rather than delegate to its parent, the class loader scoped by App1.ear. It means different WAR modules, like WAR1 and WAR2 cannot see each other's classes. The App1.ear module has its own class loader and is the parent to the WAR1 and WAR2 class loaders. The App1.ear class loader is used by the WAR1 and WAR2 class loaders; they use it when to delegate a class loading call up the hierarchy i.e. when the required class is outside of the WAR scope.

Effectively the WAR classes override the EAR classes where both exist. Finally, the EAR class loader's parent is the container class loader. The EAR class loader will delegate requests to the container class loader, but it does not do it in the same way as the WAR class loader, as the EAR class loader will prefer to delegate up rather than prefer local classes. As you can see this is getting quite hairy and is different to the normal Java class loading behavior.

Reloading Java Classes in This Application

We know from looking at the ClassLoader API earlier that you are only able to load classes. That is to say, there’s no way to unload or reload classes, so to recreate a class at runtime you have to throw away the entire class hierarchy structure and recreate it to load and use the new class, as shown in Figure 2.

image blog jrebel reload classes at runtime 2

Figure 2

If you have programmed in Java for some time you know that memory leaks do happen. Usually, it’s the case of a collection somewhere with references to objects (e.g. listeners) that should have been cleared, never were. Class loaders are a very special case of this, and unfortunately, with the current state of the Java platform, these leaks are both inevitable and costly: routinely causing OutOfMemoryErrors in applications after a small number of redeploys.

Every object had a reference to its class, which in turn had a reference to its class loader. The key part though is that every class loader, in turn, has a reference to every class it has ever loaded, each of which holds static fields defined in the class as shown in figure 3:

image blog jrebel reload classes at runtime 3

Figure 3

If a class loader is leaked it will hold on to all its classes and all their static fields. Static fields commonly hold caches, singleton objects, and various configuration and application states. Even if your application doesn’t have any large static caches, it doesn’t mean that the framework you use doesn’t hold them for you (e.g. Log4J is a common culprit as it’s often put in the server classpath). This explains why leaking a class loader can be so expensive.

To leak a class loader it’s enough to leave a reference to any object, created from a class, loaded by that class loader. Even if that object seems completely harmless (e.g. doesn’t have a single field), it will still hold on to its class loader and all the application state. A single place in the application that survives the redeploy and doesn’t do a proper cleanup is enough to sprout the leak. In a typical application, there will be several such places, some of them almost impossible to fix due to the way third-party libraries are built. Leaking a class loader is, therefore, quite common.


So it is the technical problem under the covers which means to refresh our code at runtime, we typically need to build, package, re-deploy and even restart our containers to see our updated code. However time-consuming that it.

Also, since the classloading process is so essential to the Java platform and lies at the core of everything, it cannot be easily resolved from within the platform.

The best effort to deal with reloading the classes that comes with the platform is HotSwap. It was incorporated within the Debugger API, and allowed debuggers to update class bytecode in place, using the same class identity. This meant that all objects could refer to an updated class and execute new code when their methods were called, preventing the need to reload a container whenever class bytecode was changed. Unfortunately, HotSwap's class redefinition is limited to changing method bodies only. It cannot add methods or fields or otherwise change anything else, except for the method bodies. This severely limits the usefulness of HotSwap, leaving the problem of wasting time on rebuilding your application unsolved.

 

Back to top

Final Thoughts on Reloading Java Classes

Redeploys can cost developers a significant portion of their coding time. By skipping redeploys, developers can stop waiting, and get back to what they do best -- coding great Java applications.

Want to learn more about what JRebel can offer your Java development team?

Start our "Reloading Java Classes" blog series with #101, "How to Use Java Class Loaders"

Or watch the recording of the "ZT Master Class: Intro to JRebel" where we answer the questions about how JRebel works and how you can start using it.

 Save time by automatically reloading your Java application classes at runtime with JRebel. If you'd like to see a bit more about what JRebel is capable of, explore the product page and click this button to give it a whirl:

Try JRebel For Free

Back to top