JavaOne 2011: Do You Really Get Classloaders?

After the Java Community Keynote on Thursday morning, I went to Plaza A/B in the same Hilton San Francisco to attend "Do You Really Get Classloaders?" (19220). We heard about Parleys for JavaOne 2011 in the Java Community Keynote and, coincidentally, a version of this presentation (Devoxx) is available on Parleys. The PDF of another version (Jazoon) of the presentation is available as well (there is also a Parley's video for the Jazoon version).

I knew going into "Do You Really Get Classloaders?" that my understanding of class loaders was far from complete. I had battled class loading issues enough in the past to know they are one of the more tortuous parts of Java and can be a scary experience, but I am far from an expert on their intricacies. My goal in attending this session was to improve my knowledge of class loaders and have a better understanding of how to debug issues related to class loaders.

The decently sized room was standing-room only once the presentation began. The interesting thing was that the front rows were packed and the back rows were largely empty at the beginning. I attribute this to the fact that it is so difficult to see the bottom 1/3 of the screens in this room if you're not pretty close because of too many people sitting in between you and the screen. It ended up filling up and the vast majority of the attendees stayed until the last few minutes.

The speaker, Jevgeni Kabanov, is the creator of JRebel and he stated that a free (for non-commercial use) version (JRebel Social) can be acquired. As part of working on JRebel, Kabanov had to learn many details about Java class loading ("learned a lot more about class loaders than we wanted to") and planned to share some of those lessons in this presentation. He cited presentations "Java is not type-safe" (1997) and "Dynamic classing Loading in the Java Virtual Machine" (ACM) for more details.

Kabanov started with basics of class loading. Classloaders are primarily a runtime concept that were motivated originally by Java applets. Classloaders enabled loading of one class at a time, which was important in the day of dial-up modems. This origin explains many of the positive and negative aspects of class loaders. He stated that Java tends to load classes as late as possible.

Class loaders exist in a hierarchy and every class loader has a parent class loader. The parent class loader is "consulted first" to "avoid loading same class several times." In Java EE Web Module, however, "local classes are searched first" in case container ships larger version that should be overridden by provided lighter version.

Kabanov showed a simple Java servlet example that threw a NoClassDefFoundError. One of the attendees speculated that this was due to the class, Util1, not being on the classpath. Kabanov asked how one finds out what is on the classpath. He showed a "neat little trick" that works on several servers: (URLClassLoader(getClass().getClassLoader()).getURLs()

Whenever you load a class, you can also access it as a Resource (class.getClassLoader().getResource()). Kabanov showed using this to identify location of class or extraneous map. He also talked about using javap to find out about structure of a class. He used this to show why a NoSuchMethodError was encountered in his example.

Kabanov's next example showed a funny ClassCastException: "ClassCastException: Util3 cannot be cast to Util3". The problem manifest here is that you cannot cast an instance of the class from one classloader to an instance of that class in a different classloader. The reason for this is that a class is uniquely defined by its classloader. This did not used to be the case, but that was a huge security hole called out by the previously referenced article "Java is not type-safe." The class loader is considered part of the class's identifying information so two classes with same name and same package are "different" if in different class loaders. Kabanov was able to cause this exception in this case because the Web class loader loads its own version rather than delegating to its parent.

Kabanov demonstrated an LinkageError ("loader constraint violation") by removing a class cast. This too was covered in the "Java is not type-safe" paper and the point is that it's not just a class cast issue. In this case, the JVM internal implementation is trying to implicitly cast the classes and they are still not the same due to different class loaders.

In another example, Kabanov produced an IllegalAccessError. This turned out to be a case of the same class name with a different packages. It is an example of the problem of "more than one class found" category which includes LinkageError, ClassCastException, and IllegalAccessError.

Kabanov stated several times that one of the best ways to identify some of these class loader issues is use of -verbose:class on the JVM from the command line (Java command launcher, java). As he covered the different categories of class loader issues, he looked at various other tools to determine the problem. These include use container-specific logs and use of the Unix/Linux find command. Kabanov also highlighted the freely available Eclipse Memory Analyzer for hunting down memory leaks (this tool came up at last year's JavaOne as well).

The Tragedy of the ClassLoaders is that every object holds onto the entire class loader and this is what makes it tricky. It is difficult to build tools to detect and resolve leaking of class loaders. Kabanov has a blog post on web environment class reloading called Reloading Java Classes 301: Classloaders in Web Development — Tomcat, GlassFish, OSGi, Tapestry 5 and so on.

Kabanov says that "the modern way" is to have a classloader per JAR, to have all class loaders as siblings, and to have each JAR explicitly state which packages it imports and exports. Kabanov discussed several ways forward including in-app updates. He referenced LiveRebel to be used for making small updates cheap.

Original posting available at (Inspired by Actual Events)