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

Do You Really Get Classloaders?

Part I: An Overview of Java Classloaders, Delegation and Common Problems

In this part, we provide an overview of classloaders, explain how delegation works and examine how to solve common problems that Java developers encounter with classloaders on a regular basis.

Introduction: Why you should know, and fear, Classloaders

Classloaders are at the core of the Java language. Java EE containers, OSGi, various web frameworks and other tools use classloaders heavily. Yet, something goes wrong with classloading, would you know how to solve it?

Join us for a tour of the Java classloading mechanism, both from the JVM and developer point-of-view. We will look at typical problems related to classloading and how to solve them. NoClassDefFoundError, LinkageError and many others are symptoms of specific things going wrong that you can usually find and fix. For each problem, we’ll go through an example with a corresponding solution. We’ll also take a look at how and why classloaders leak and how can that be remedied.

And for dessert, we 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, explain the reloading process, and then proceed to a specific example to illustrate typical problems and solutions.

Enter java.lang.ClassLoader

Let’s dive into the beautiful world of classloader mechanics.

It’s important to realize that each classloader is itself an object–an instance of a class that extends java.lang.ClassLoader. Every class is loaded by one of those instances and developers are free to subclass java.lang.ClassLoader to extend the manner in which the JVM loads classes.

There might be a little confusion: if a classloader has a class and every class is loaded by a classloader, then what comes first? We need an understanding of the mechanics of a classloader (by proxy of examining its API contract) and the JVM classloader hierarchy.

First, here is the API, with some less relevant parts omitted:

package java.lang;

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()

By far, the most important method of java.lang.ClassLoader is the loadClass method, which takes the fully qualified name of the class to be loaded and returns an object of class Class.

The defineClass method is used to materialize a class for the JVM. The byte array parameter of defineClass is the actual class byte code loaded from disk or any other location.

What if you no longer had to redeploy your Java code to see changes? The choice is yours. In just a few clicks you can Say Goodbye to Java Redeploys forever.

getResource and getResources return URLs to actually existing resources when given a name or a path to an expected resource. They are an important part of the classloader contract and have to handle delegation the same way as loadClass – delegating to the parent first and then trying to find the resource locally. We can even view loadClass as being roughly equivalent to defineClass(getResource(name).getBytes()).

The getParent method returns the parent classloader. We’ll have a more detailed look at what that means in the next section.

The lazy nature of Java has an effect on how do classloaders work – everything should be done at the last possible moment. A class will be loaded only when it is referenced somehow – by calling a constructor, a static method or field.

Now let’s get our hands dirty with some real code. Consider the following example: class A instantiates class B.

	public class A {
	  public void doSomething() {
  	 B b = new B();

The statement B b = new B() is semantically equivalent to B b = A.class. getClassLoader().loadClass(“B”).newInstance()

As we see, every object in Java is associated with its class (A.class) and every class is associated with classloader (A.class.getClassLoader()) that was used to load the class.

When we instantiate a ClassLoader, we can specify a parent classloader as a constructor argument. If the parent classloader isn’t specified explicitly, the virtual machine’s system classloader will be assigned as a default parent. And with this note, let’s examine the classloader hierarchy of a JVM more closely.