Class Loading Subsystem
Role of the Class Loader
- Responsible for loading class files from file systems or networks
- Class loading is distinct from execution, which is handled by the Execution Engine
- Loaded class metadata is stored in the Method Area
Class Loading Process
Consider the following simple Java class:
public class HelloLoader {
public static void main(String[] args) {
System.out.println("ClassLoader loaded me...");
System.out.println("I'll repay this kindness in my next life!");
}
}
The loading process involves:
- Loading the
HelloLoaderclass before executing themain()method - Linking and initialization if loading is successful
- Throwing an exception if loading fails
Phases of Class Loading
- Loading:
- Obtain binary byte stream by fully qualified class name
- Convert static storage structure to runtime data structure in Method Area
- Create a
java.lang.Classobject in memory as the access entry
- Linking:
- Verification: Ensure byte stream complies with JVM requirements
- Preparation: Allocate memory for class variables and set default values
- Resolution: Convert symbolic references in constant pool to direct references
- Initialization:
- Execute class constructor
<clinit>() - Triggered by class instantiation, static method calls, or reflection
- Execute class constructor
Example: Class Initialization Behavior
public class ClassInitTest {
private static int num = 1;
static {
num = 2;
number = 20;
System.out.println(num);
}
private static int number = 10;
public static void main(String[] args) {
System.out.println(ClassInitTest.num); // Output: 2
System.out.println(ClassInitTest.number); // Output: 10
}
}
Types of Class Loaders
- Bootstrap ClassLoader (C/C++): Loads core Java libraries
- Extension ClassLoader: Loads from
jre/lib/extorjava.ext.dirs - Application ClassLoader: Loads from classpath
Example: ClassLoader Hierarchy
public class ClassLoaderTest {
public static void main(String[] args) {
ClassLoader systemLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemLoader); // AppClassLoader
ClassLoader extLoader = systemLoader.getParent();
System.out.println(extLoader); // ExtClassLoader
ClassLoader bootstrapLoader = extLoader.getParent();
System.out.println(bootstrapLoader); // null
System.out.println(String.class.getClassLoader()); // null (Bootstrap)
}
}
Custom Class Loaders
Use cases include:
- Isolating classes with conflicting names
- Loading classes from alternative sources (e.g., network, database)
- Implementing class encryption/decryption
Implementing a Custom ClassLoader
public class CustomClassLoader extends ClassLoader {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] classBytes = loadFromCustomSource(name);
if (classBytes == null) throw new ClassNotFoundException(name);
return defineClass(name, classBytes, 0, classBytes.length);
} catch (Exception e) {
throw new ClassNotFoundException(name, e);
}
}
private byte[] loadFromCustomSource(String name) {
// Implementation to load and potentially decrypt bytecode
return null;
}
}
Parent Delegation Model
Key principles:
- Delegates class loading to parent class loader first
- Only loads class itself if parent fails
- Prevents duplicate class loading
- Protects core libraries from modification
Example: Preventing Core Class Modification
public class StringTest {
public static void main(String[] args) {
java.lang.String str = new java.lang.String();
System.out.println("hello,world");
}
}
A custom java.lang.String will not be loaded due to Bootstrap ClassLoader precedence
Class Identity Determination
Two classes are considered identical in the JVM if:
- They have the same fully qualified name
- They are loaded by the same ClassLoader instance