http://www.ibm.com/developerworks/java/library/j-dclp2/index.html
An in-depth look at some simple class loading quirks and conundrums
Summary: This four-part article series examines Java™ class loading to help application developers understand and debug problems they may encounter. In Part 2, authors Lakshmi Shankar and Simon Burns from the IBM Hursley Labs tackle some exceptions that, while fairly simple, often puzzle novice and experienced Java developers alike.
Date: 06 Dec 2005
Level: Intermediate
Comments: 0 (View | Add comment - Sign in)
Rate this article
This article, the second in a series of four, looks at the various class loading exceptions typically thrown when running applications. These exceptions, though commonly seen, are often not well understood by Java developers. Taking each exception in turn, this article provides detailed examples that highlight their behavior, explain their causes, and show some possible resolution techniques. The article starts with the very common ClassNotFoundException
and moves onto less well known exceptions, such as ExceptionInInitializerError
.
Before you begin this article, you should be familiar with the class loader delegation model, along with the phases and stages of class linking. We recommend that you start by reading the first article in this series.
ClassNotFoundException
is the most common type of class loading exception. It occurs during the loading phase. The Java specification describes ClassNotFoundException
as follows:
Thrown when an application tries to load in a class through its string name using:but no definition for the class with the specified name could be found.
- The
forName()
method in classClass
.- The
findSystemClass method()
in classClassLoader
.- The
loadClass()
method in classClassLoader
.
So a ClassNotFoundException
is thrown if an explicit attempt to load a class fails. The test case in Listing 1 provides example code that throws a ClassNotFoundException
:
Listing 1. ClassNotFoundExceptionTest.java
import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; public class ClassNotFoundExceptionTest { public static void main(String args[]) { try { URLClassLoader loader = new URLClassLoader(new URL[] { new URL( "file://C:/CL_Article/ClassNotFoundException/")}); loader.loadClass("DoesNotExist"); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (MalformedURLException e) { e.printStackTrace(); } } } |
This test case defines a class loader (MyClassLoader
), which is used to load a non-existent class (DoesNotExist
). When it is run, the following exception occurs:
java.lang.ClassNotFoundException: DoesNotExist at java.net.URLClassLoader.findClass(URLClassLoader.java:376) at java.lang.ClassLoader.loadClass(ClassLoader.java:572) at java.lang.ClassLoader.loadClass(ClassLoader.java:504) at ClassNotFoundExceptionTest.main(ClassNotFoundExceptionTest.java:11) |
A ClassNotFoundException
is thrown because the test attempts the load using an explicit call to loadClass()
.
By throwing a ClassNotFoundException
, the class loader informs you that the bytecode required to define the class is simply not present in the locations where the class loader is looking for it. These exceptions are usually simple to fix. You can ensure that the classpath being used is set as expected by checking it using the IBM verbose option. (For more on this option, see the first article in this series.) If the classpath is set correctly but you're still seeing the error, then the desired class is not present on the classpath. To fix this, either move the class into a directory or JAR file specified in the classpath, or add the location where the class is stored to the classpath.
NoClassDefFoundError
is another common exception thrown by the class loader during the loading phase. The JVM specification defines NoClassDefFoundError
as follows:
Thrown if the Java virtual machine or aClassLoader
instance tries to load in the definition of a class (as part of a normal method call or as part of creating a new instance using the new expression) and no definition of the class could be found.
The searched-for class definition existed when the currently executing class was compiled, but the definition can no longer be found.
Essentially, this means that a NoClassDefFoundError
is thrown as a result of a unsuccessful implicit class load.
The test case in Listings 2 through 4 produce a NoClassDefFoundError
because an implicit load of class B
will fail:
Listing 2. NoClassDefFoundErrorTest.java
public class NoClassDefFoundErrorTest { public static void main(String[] args) { A a = new A(); } } |
Listing 3. A.java
public class A extends B { } |
Listing 4. B.java
public class B { } |
Once you've compiled the code in these listings, remove the classfile for B
. When the code is executed, the following error occurs:
Exception in thread "main" java.lang.NoClassDefFoundError: B at java.lang.ClassLoader.defineClass0(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:810) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:147) at java.net.URLClassLoader.defineClass(URLClassLoader.java:475) at java.net.URLClassLoader.access$500(URLClassLoader.java:109) at java.net.URLClassLoader$ClassFinder.run(URLClassLoader.java:848) at java.security.AccessController.doPrivileged1(Native Method) at java.security.AccessController.doPrivileged(AccessController.java:389) at java.net.URLClassLoader.findClass(URLClassLoader.java:371) at java.lang.ClassLoader.loadClass(ClassLoader.java:572) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:442) at java.lang.ClassLoader.loadClass(ClassLoader.java:504) at NoClassDefFoundErrorTest.main(NoClassDefFoundErrorTest.java:3) |
Class A
extends class B
; so, when class A
is being loaded, the class loader implicitly loads class B
. Because class B
is not present, a NoClassDefFoundError
is thrown. If the class loader had been explicitly told to load class B
(by a loadClass("B")
call, for instance), a ClassNotFoundException
would have been thrown instead.
Obviously, to fix the problem in this particular example, class B
must be present on the classpath of a suitable class loader. This example may seem trivial and unrealistic; however, in complex, real-world systems with many classes, situations like this can happen when classes are missed during packaging or deployment.
In this example, A
extends B
; however, the same error would still occur if A
referenced B
in any other way -- as a method parameter, for example, or as an instance field. If the relationship between the two classes were one of reference rather than of inheritance, then the error would be thrown on the first active use of A
, rather than during the loading of A
.
Another exception that can be thrown by the class loader is ClassCastException
. It is thrown as a result of incompatible types being found in a type comparison. The JVM specification says a that ClassCastException
is:
Thrown to indicate that the code has attempted to cast an object to a subclass of which it is not an instance.
Listing 5 illustrates an example of code that raises a ClassCastException
:
Listing 5. ClassCastException.java
public class ClassCastExceptionTest { public ClassCastExceptionTest() { } private static void storeItem(Integer[] a, int i, Object item) { a[i] = (Integer) item; } public static void main(String args[]) { Integer[] a = new Integer[3]; try { storeItem(a, 2, new String("abc")); } catch (ClassCastException e) { e.printStackTrace(); } } } |
In Listing 5, the storeItem()
method is called, passing in an Integer
array, an int
, and a string. However, internally, the method does two things:
- It implicitly casts the
String
object type to anObject
type (for the parameter list). - It explicitly casts this
Object
type to anInteger
type (in the method definition).
When the program is run, the following exception occurs:
java.lang.ClassCastException: java.lang.String at ClassCastExceptionTest.storeItem(ClassCastExceptionTest.java:6) at ClassCastExceptionTest.main(ClassCastExceptionTest.java:12) |
The exception is thrown by the explicit cast because the test case is trying to convert something of type String
to an Integer
.
Given an object being tested (such as item
in Listing 5) and a target class (Integer
) being cast to, the class loader checks the following rules:
- For a normal object (non-array): The object must be an instance of the target class or a subclass of the target class. If the target class is an interface, then it is considered a subclass if it implements that interface.
- For an array type: The target class must be the array type or
java.lang.Object
,java.lang.Cloneable
, orjava.io.Serializable
.
If either of the above rules are violated, then a ClassCastException
is thrown by the class loader. The easiest way to fix such exceptions is to carefully check that the type to which an object is being cast conforms to the rules mentioned above. In some cases, it may be sensible to use an instanceof
check prior to doing a class cast.
The class loader plays an important role in linking a native call to its appropriate native definition. An UnsatisfiedLinkError
occurs during the resolving stage of the linking phase when a program tries to load an absent or misplaced native library. The JVM specification says that an UnsatisfiedLinkError
is:
Thrown if the Java Virtual Machine cannot find an appropriate native language definition of a method declared
native
.
When a native method is invoked, the class loader attempts to load the native library that defines that method. If this library is not found, the error is thrown.
Listing 6 illustrates a test case that throws an UnsatisfiedLinkError
:
Listing 6. UnsatisfiedLinkError.java
public class UnsatisfiedLinkErrorTest { public native void call_A_Native_Method(); static { System.loadLibrary("myNativeLibrary"); } public static void main(String[] args) { new UnsatisfiedLinkErrorTest().call_A_Native_Method(); } } |
This code invokes the native method call_A_Native_Method()
, which is defined in the native library myNativeLibrary
. Because this library does not exist, the following error occurs when the program is run:
The java class could not be loaded. java.lang.UnsatisfiedLinkError: Can't find library myNativeLibrary (myNativeLibrary.dll) in sun.boot.library.path or java.library.path sun.boot.library.path=D:\sdk\jre\bin java.library.path= D:\sdk\jre\bin at java.lang.ClassLoader$NativeLibrary.load(Native Method) at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:2147) at java.lang.ClassLoader.loadLibrary(ClassLoader.java:2006) at java.lang.Runtime.loadLibrary0(Runtime.java:824) at java.lang.System.loadLibrary(System.java:908) at UnsatisfiedLinkErrorTest.<clinit>(UnsatisfiedLinkErrorTest.java:6) |
The loading of a native library is initiated by the class loader of the class that calls System.loadLibrary()
-- the class loader of UnsatisfiedLinkErrorTest
, in Listing 6. Depending on what class loader this is, different locations are searched:
- For classes loaded by the bootstrap class loader,
sun.boot.library.path
is searched. - For classes loaded by the extension class loader,
java.ext.dirs
is searched first, followed bysun.boot.library.path
and thenjava.library.path
. - For classes loaded by the system class loader,
sun.boot.library.path
is searched, followed byjava.library.path
.
In Listing 6, the UnsatisfiedLinkErrorTest
class is loaded by the system class loader. To load the referenced native library, this class loader looks in the sun.boot.library.path
and then in the java.library.path
. Because the library is not available in either of these locations, the class loader throws the UnsatisfiedLinkageError
.
Once you understand the class loaders involved in the loading of the library, you can resolve these types of problems by placing the library in an appropriate location.
The JVM specification says a ClassCircularityError
is thrown if:
A class or interface could not be loaded because it would be its own superclass or superinterface.
This error is thrown during the resolving stage of the linking phase. It is a slightly odd error because the Java compiler does not allow such a circular situation to arise. However, the error could occur if you were to separately compile classes and then bring them together. Imagine the following scenario. First, compile the classes in Listings 7 and 8:
Listing 7. A.java
public class A extends B { } |
Listing 8. B.java
public class B { } |
Then, separately compile the classes in Listings 9 and 10:
Listing 9. A.java
public class A { } |
Listing 10. B.java
public class B extends A { } |
Finally, take class A
from Listing 7 and class B
from Listing 10 and run an application that tries to load either A
or B
. This may seem like an unlikely situation, but something very much like it could occur in a complex system where many different parts are brought together.
Obviously, to fix this problem, you must avoid the cyclic class hierarchy.
The JVM specification states that a ClassFormatError
is thrown if:
The binary data that purports to specify a requested compiled class or interface is malformed.
This exception is thrown during the verification stage of the linking phase of class loading. The binary data can be malformed if the bytecodes have been changed -- if the major or minor number has been changed, for instance. This could occur if the bytecodes had been deliberately hacked, for example, or if an error had occurred when transferring the class file over a network.
The only way to fix this problem is to obtain a corrected copy of the bytecodes, possibly by recompiling.
According to the JVM specification, an ExceptionInInitializer
is thrown:
- If an initializer completes abruptly by throwing some exception
E
, and if the class ofE
is notError
or one of its subclasses, then a new instance of the classExceptionInInitializerError
, withE
as the argument, is created and used in place ofE
.
- If the Java Virtual Machine attempts to create a new instance of the class
ExceptionInInitializerError
but is unable to do so because anOut-Of-Memory-Error
occurs, then theOutOfMemoryError
object is thrown instead.
The code in Listing 8 throws an ExceptionInInitializerError
:
Listing 8. ExceptionInInitializerErrorTest.java
public class ExceptionInInitializerErrorTest { public static void main(String[] args) { A a = new A(); } } class A { // If the SecurityManager is not turned on, a // java.lang.ExceptionInInitializerError will be thrown static { if(System.getSecurityManager() == null) throw new SecurityException(); } } |
When an exception occurs in the static code block, it is automatically caught and wrapped with an ExceptionInInitializerError
. This can be seen in the output below:
Exception in thread "main" java.lang.ExceptionInInitializerError at ExceptionInInitializerErrorTest.main(ExceptionInInitializerErrorTest.java:3) Caused by: java.lang.SecurityException at A.<clinit>(ExceptionInInitializerErrorTest.java:12) ... 1 more |
This error is thrown during the initialization phase of class loading. The way to fix it is to examine the exception that caused the ExceptionInInitializerError
(shown in the stack trace under Caused by:
) and find a way to stop this exception from being thrown.
In this article, you learned about the various class loading exceptions, from the most basic errors to some more cryptic ones. In the next article in this series, we will look at some other class loading problems that you might encounter when running more complex applications.
Learn
- Demystifying class loading problems: Read the complete series.
- "Understanding the Java ClassLoader" (Greg Travis developerWorks, April 2001): An introduction to class loading.
- "Java programming dynamics, Part 1: Classes and class loading" (Dennis Sosnoski developerWorks, April 2003): Understand class loading issues ranging from the number of classes required for running a simple Java application to the class loader conflicts that can cause problems in J2EE and similar complex architectures.
- JVM specification: Comprehensive coverage of the JVM class file format and instruction set from the source.
- IBM Diagnostics Guides: Learn more about debugging.
- Persistent Reusable JVM: Online book that introduces the concept of Persistent Reusable JVMs and provides technical guidance on writing middleware and applications to run on them.
- The Java technology zone: Hundreds of articles about every aspect of Java programming.
Get products and technologies
- IBM Java developer kits: Create and test J2SE applications on some of IBM's most popular platforms.
Discuss