On this page
Class Loading
Class loading is the process of finding bytecode for a class and making it available to the JVM. It happens lazily — classes are loaded when first referenced.
Loading Process
- Loading — Read
.classfile bytes, createClass<?>object - Linking
- Verify — Ensure bytecode is valid and safe
- Prepare — Allocate memory for static fields, set default values
- Resolve — Replace symbolic references with direct references (optional)
- Initialization — Execute static initializers and assign static fields
public class Demo {
static {
System.out.println("Class initialized"); // runs once on first use
}
}
A class is loaded when:
new Demo()is called- A static field or method is accessed
Class.forName("Demo")is invoked- A subclass is loaded (parent loads first)
Class Loader Hierarchy
Bootstrap ClassLoader (null — loads core JDK classes)
↑
Platform/Extension ClassLoader (loads ext dirs, Java modules)
↑
Application/System ClassLoader (loads classpath)
↑
Custom ClassLoaders
ClassLoader app = ClassLoader.getSystemClassLoader();
ClassLoader platform = app.getParent();
ClassLoader bootstrap = platform.getParent(); // null
Delegation Model
Class loaders follow the parent delegation model:
- Ask the parent to load the class
- If parent cannot find it, load it yourself
This ensures core classes (java.lang.String) are always loaded by the bootstrap loader — preventing spoofing.
public class LoadDemo {
public static void main(String[] args) throws ClassNotFoundException {
Class<?> clazz = Class.forName("java.util.ArrayList");
System.out.println(clazz.getClassLoader()); // null (bootstrap)
}
}
Custom Class Loader
Load classes from non-standard sources (network, encrypted files, plugins):
public class PluginClassLoader extends ClassLoader {
private final Path pluginDir;
public PluginClassLoader(Path pluginDir, ClassLoader parent) {
super(parent);
this.pluginDir = pluginDir;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Path classFile = pluginDir.resolve(name.replace('.', '/') + ".class");
try {
byte[] bytes = Files.readAllBytes(classFile);
return defineClass(name, bytes, 0, bytes.length);
} catch (IOException e) {
throw new ClassNotFoundException(name, e);
}
}
}
Usage:
PluginClassLoader loader = new PluginClassLoader(
Path.of("plugins"), ClassLoader.getSystemClassLoader());
Class<?> pluginClass = loader.loadClass("com.example.MyPlugin");
Object plugin = pluginClass.getDeclaredConstructor().newInstance();
Class.forName vs ClassLoader.loadClass
// Initializes the class
Class<?> c1 = Class.forName("com.example.Service");
// Loads but does NOT initialize
Class<?> c2 = ClassLoader.getSystemClassLoader()
.loadClass("com.example.Service");
Common Issues
| Issue | Cause |
|---|---|
ClassNotFoundException |
Class not on classpath |
NoClassDefFoundError |
Class was found at compile time but missing at runtime |
ClassCastException (same class name) |
Same class loaded by different class loaders |
| Metaspace OOM | Too many classes loaded dynamically |
Best Practices
- Understand the delegation model before writing custom class loaders
- Always specify a parent class loader for custom loaders
- Use plugin architectures (OSGi, Java Module System) for complex class loading needs
- Be cautious with dynamic class loading — it consumes metaspace