WebSphere Development
Demystifying Class Loading Problems
Part Two of a Four-Part Article
Apr. 19, 2006 10:00 AM
Digg This!
Page 2 of 3
« previous page
next page »
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 {
}
Page 2 of 3
« previous page
next page »
About Lakshmi ShankarLakshmi 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.
About Simon BurnsSimon 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.