Demystifying class loading problems, Part 2: Basic class loading exceptions

 

http://www.ibm.com/developerworks/java/library/j-dclp2/index.html

 

An in-depth look at some simple class loading quirks and conundrums

Simon Burns ( simon_burns@uk.ibm.com), Java Technology Center Development Team, IBM Hursley Labs
Simon Burns
Simon Burns was the component owner and team lead for the Persistant Reusable JVM in the Java Technology Centre in IBM Hursley Labs. He worked in JVM development for over three years, specializing in the Persistant Reusable JVM technology and the z/OS platform. He has also worked closely with CICS, helping them to exploit this technology. Simon worked on the OSGi framework as part of the open-source Eclipse Equinox project, which has now been integrated into Eclipse 3.1. He is now working on componentization.
Lakshmi Shankar ( shankarl@uk.ibm.com), Java Technology Center Development Team, IBM Hursley Labs
Lakshmi Shankar
Lakshmi Shankar is a Software Engineer in IBM Hursley Labs, UK. He has worked for IBM for more than three years and has a broad range of experience, having worked in Java performance, test, and development within Hursley Labs. Until recently he was the Class Loading component owner for IBM's Java technology. He is now a developer working as part of the Information Management team.

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.

View more content in this series

Date:  06 Dec 2005
Level:  Intermediate

Comments:   0 (View | Add comment - Sign in)

Average rating 4 stars based on 9 votes Average rating (9 votes)
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

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:
  • The forName() method in class Class.
  • The findSystemClass method() in class ClassLoader.
  • The loadClass() method in class ClassLoader.
but no definition for the class with the specified name could be found.

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

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 a ClassLoader 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.


ClassCastException

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 an Object type (for the parameter list).
  • It explicitly casts this Object type to an Integer 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, or java.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.


UnsatisfiedLinkError

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 by sun.boot.library.path and then java.library.path.
  • For classes loaded by the system class loader, sun.boot.library.path is searched, followed by java.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.


ClassCircularityError

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.


ClassFormatError

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.


ExceptionInInitializerError

According to the JVM specification, an ExceptionInInitializer is thrown:

  • If an initializer completes abruptly by throwing some exception E, and if the class of E is not Error or one of its subclasses, then a new instance of the class ExceptionInInitializerError, with E as the argument, is created and used in place of E.

  • If the Java Virtual Machine attempts to create a new instance of the class ExceptionInInitializerError but is unable to do so because an Out-Of-Memory-Error occurs, then the OutOfMemoryError 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.


What's next

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.


Resources

Learn

Get products and technologies

Discuss

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值