Imagine a bacon-wrapped Ferrari. Still not better than our free technical reports.
See all our reports

Reloading Java Classes 101: Objects, Classes and ClassLoaders

Welcome to Turnaround article series from ZeroTurnaround.

In this article we will review how to reload a Java class using a dynamic classloader. To get there we’ll see how objects, classes and classloaders are tied to each other and the process required to make changes. We begin with a bird’s eye view of the problem, explains the reloading process, and then proceed to a specific example to illustrate typical problems and solutions. Other articles in the series include:

A video presentation “Do you really get class loaders” by Jevgeni Kabanov

A Bird’s Eye View

The first thing to understand when talking about reloading Java code is the relation between classes and objects. All Java code is associated with methods contained in classes. Simplified, you can think of a class as a collection of methods, that receive “this” as the first argument. The class with all its methods is loaded into memory and receives a unique identity. In the Java API this identity is represented by an instance of java.lang.Class that you can access using the MyObject.class expression.

Every object created gets a reference to this identity accessible through the Object.getClass() method. When a method is called on an object, the JVM consults the class reference and calls the method of that particular class. That is, when you call mo.method() (where mo is an instance of MyObject), then the JVM will call mo.getClass().getDeclaredMethod("method").invoke(mo) (this is not what the JVM actually does, but the result is the same).

object

Every Class object is in turn associated with its classloader (MyObject.class.getClassLoader()). The main role of the class loader is to define a class scope — where the class is visible and where it isn’t. This scoping allows for classes with the same name to exist as long as they are loaded in different classloaders. It also allows loading a newer version of the class in a different classloader.

reloading-object

The main problem with code reloading in Java is that although you can load a new version of a class, it will get a completely different identity and the existing objects will keep referring the previous version of the class. So when a method is called on those objects it will execute the old version of the method.

Let’s assume that we load a new version of the MyObject class. Let’s refer to the old version as MyObject_1 and to the new one as MyObject_2. Let’s also assume that MyObject.method() returns “1” in MyObject_1 and “2” in MyObject_2. Now if mo2 is an instance of MyObject_2:

  • mo.getClass() != mo2.getClass()
  • mo.getClass().getDeclaredMethod("method").invoke(mo)
    != mo2.getClass().getDeclaredMethod("method").invoke(mo2)
  • mo.getClass().getDeclaredMethod("method").invoke(mo2) throws a ClassCastException, because the Class identities of mo and mo2 do no match.

This means that any useful solution must create a new instance of mo2 that is an exact copy of mo and replace all references to mo with it. To understand how hard it is, remember the last time you had to change your phone number. It’s easy enough to change the number itself, but then you have to make sure that everyone you know will use the new number, which is quite a hassle. It’s just as difficult with objects (in fact, it’s actually impossible, unless you control the object creation yourself), and we’re talking about many objects that you must update at the same time.

Down and Dirty

Let’s see how this would look in code. Remember, what we’re trying to do here is load a newer version of a class, in a different classloader. We’ll use an Example class that looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
public class Example implements IExample {
  private int counter;
  public String message() {
    return "Version 1";
  }
  public int plusPlus() {
    return counter++;
  }
  public int counter() {
    return counter;
  }
}

We’ll use a main() method that will loop infinitely and print out the information from the Example class. We’ll also need two instances of the Example class: example1 that is created once in the beginning and example2 that is recreated on every roll of the loop:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Main {
  private static IExample example1;
  private static IExample example2;
 
  public static void main(String[] args)  {
    example1 = ExampleFactory.newInstance();
 
    while (true) {
      example2 = ExampleFactory.newInstance();
 
      System.out.println("1) " +
        example1.message() + " = " + example1.plusPlus());
      System.out.println("2) " +
        example2.message() + " = " + example2.plusPlus());
      System.out.println();
 
      Thread.currentThread().sleep(3000);
    }
  }
}

IExample is an interface with all the methods from Example. This is necessary because we’ll be loading Example in an isolated classloader, so Main cannot use it directly (otherwise we’d get a ClassCastException).

1
2
3
4
public interface IExample {
  String message();
  int plusPlus();
}

From this example, you might be surprised to see how easy it is to create a dynamic class loader. If we remove the exception handling it boils down to this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ExampleFactory {
  public static IExample newInstance() {
    URLClassLoader tmp =
      new URLClassLoader(new URL[] {getClassPath()}) {
        public Class loadClass(String name) {
          if ("example.Example".equals(name))
            return findClass(name);
          return super.loadClass(name);
        }
      };
 
    return (IExample)
      tmp.loadClass("example.Example").newInstance();
  }
}

The method getClassPath() for the purposes of this example could return the hardcoded classpath. However, in the full source code (available in the Resources section below) you can see how we can use the ClassLoader.getResource() API to automate that.

Now let’s run Main.main and see the output after waiting for a few loop rolls:

1) Version 1 = 3
2) Version 1 = 0

As expected, while the counter in the first instance is updated, the second stays at “0”. If we change the Example.message() method to return “Version 2″. The output will change as follows:

1) Version 1 = 4
2) Version 2 = 0

As we can see, the first instance continues incrementing the counter, but uses the old version of the class to print out the version. The second instance class was updated, however all of the state is lost.

To remedy this, let’s try to reconstruct the state for the second instance. To do that we can just copy it from the previous iteration.

First we add a new copy() method to Example class (and corresponding interface method):

1
2
3
4
5
public IExample copy(IExample example) {
  if (example != null)
    counter = example.counter();
  return this;
}

Next we update the line in the Main.main() method that creates the second instance:

9
example2 = ExampleFactory.newInstance().copy(example2);

Now waiting for a few iterations yields:

1) Version 1 = 3
2) Version 1 = 3

And changing Example.message() method to return “Version 2″ yields:

1) Version 1 = 4
2) Version 2 = 4

As you can see even though it’s possible for the end user to see that the second instance is updated and all its state is preserved, it involves managing that state by hand. Unfortunately, there is no way in the Java API to just update the class of an existing object or even reliably copy its state, so we will always have to resort to complicated workarounds.

In subsequent articles we’ll review how web containers, OSGi, Tapestry 5, Grails and others confront the problem of managing state when reloading classes, then we’ll dig into how HotSwap, Dynamic Languages, and the Instrumentation API work, and go behind the scenes with JRebel as well.

Resources

Other Articles in the Reloading Java Classes Series

  • Cem Koc

    Excellent!!!

    Maladets Jevgeni !

  • Rob

    It seems that the example works only if the class is in the main classpath (i.e. in “./example” directory).

    If you put the class in a different directory, the code breaks with ClassNotFoundException, even if you modify the URL generation code.

    Why is that?

    //cp = new URL(loc.substring(0, loc.length() – resName.length()));
    cp = new URL(“file:/D:/temp”);

    [java] Exception in thread “Thread-0″ java.lang.RuntimeException: java.lang.ClassNotFoundException: example.Example
    [java] at example.DynaClassFactory.newInstance(DynaClassFactory.java:32)
    [java] at example.Main.run(Main.java:19)
    [java] at java.lang.Thread.run(Thread.java:619)

  • http://www.ekabanov.net Jevgeni Kabanov

    @Rob

    “example” is a Java package and is part of the class name. When URLClassLoader looks for example.Example it really searches all classpath URLs for “example/Example.class”.

  • Rob

    Hi Evgenij,

    Understood – this is exactly what I tried.
    It seems that if the example.Example is in the \launching classpath\ – i.e. \.\ or root, it works.

    However, If i place the example/Example somewhere else, and then use:

    = new URL(\file:/Location off class path\);

    The code breaks.

    Do you have an explanation for this behavior?

    Thank you.

  • Pingback: Speeding up development with JRebel()

  • Pingback: Reloading Java Classes 201: How do ClassLoader leaks happen? | ZeroTurnaround.com()

  • Pingback: Reloading Java Classes 301: Classloaders in Web Development — Tomcat, GlassFish, OSGi, Tapestry 5 and so on | ZeroTurnaround.com()

  • Pingback: Reloading Java Classes 401: HotSwap and JRebel — Behind the Scenes | ZeroTurnaround.com()

  • Pingback: RJC501: How Much Does Turnaround Cost? | ZeroTurnaround.com()

  • http://fiveholiday55.blogspot.com Helen Neely

    This is great, I saw the link on TheServerSide and knew I had to check it out.
    Nice article

  • Stephen

    This example does not seems right to me, example2 is getting instantiated every time inside while loop where in example1 is outside while loop. You are playing with instances not with classloaders. This example does not support explanation given above.

  • Pingback: JRebel实践 part2 behind the scene | Tuo_Huang()

  • Hdh97

    很好的一个网站!

  • bob

    Thread.currentThread().sleep(3000);

    should be written as
    Thread.sleep(3000);since sleep is a static method

  • Anonymous

    Hi Jevgeni,

    This was a really great presentation. Thank you a lot for it.

    I have only one remark about minute 24:30:

    You say that it was Joshua Bloch or Gilad Bracha that published a paper
    about the brokenness of Java classes. While both of them have numerous
    contributions to the Java platform I think the paper you mean is:

    Vijay Sarswat: Java is not type-safe.

    The article is available here: http://www.cis.upenn.edu/~bcpierce/courses/629/papers/Saraswat-javabug.html

    This is an older paper by Vijay and he has not put it on his site, I will write to him about this.

    I am mentioning this because researchers live and die by their publications and it just it

    I am giving the abstract here:

    Abstract:
    A language is type-safe if the only operations that can be
    performed on data in the language are those sanctioned by the type of
    the data. Java is not type-safe, though it was intended to be. A Java
    object may read and modify fields (and invoke methods) private to
    another object. It may read and modify internal Java Virtual Machine
    (JVM) data-structures. It may invoke operations not even defined for
    that object, causing completely unpredictable results, including JVM
    crashes (core dumps). Thus Java security, which depends strongly on
    type-safety, is completely compromised.

     

  • Pingback: Reporte de las: 2012-07-28 16:00:14.0 | Pura Politica()

  • Pingback: Reporte de las: 2012-07-28 17:00:14.0 | Pura Politica()

  • Pingback: Reporte de las: 2012-07-28 18:00:14.0 | Pura Politica()

  • Pingback: Reporte de las: 2012-07-28 19:00:13.0 | Pura Politica()

  • Pingback: JRebel 101: What JRebel is and how it makes Java development lightning fast | zeroturnaround.com()

  • Pingback: JRebel | Liu Wei()

  • Pingback: JRebel Reloading Java Classes | Liu Wei()

  • Amil

    Thank you for this nice guide.

  • Rohit5

    Agreed, I always confused in this topic despite reading several articles on Java classloaders but I am getting it kind of and this presentation help me immensely. Thank you.

  • asdfa

    ulol

  • 旸 杨

    Hi, for Example class, the private field counter, do you original mean private staitc instead?? otherwise there is no meaning of this , object is created each time of the loop ,even using same classload we will get the exactly the same result.

  • Alin Alx

    Hi Jevgeni !

    There is something I do not understand , so please be so kind to explain this : At some point you say :

    “If we change the Example.message() method to return “Version 2″. The output will change as follows:

    1) Version 1 = 4
    2) Version 2 = 0

    As we can see, the first instance continues incrementing the counter,
    but uses the old version of the class to print out the version”
    How could you change the returned value only for a single instance of the same class ? How could we have the old version of the class for example1 and the new one for example2 , in the same run of the program ?
    Thank you !