Bootstrap, Extension, and Application Classloaders in Java

Java uses a hierarchical classloading system with three main classloaders that work in a parent-delegation model.

ClassLoader Hierarchy

Bootstrap ClassLoader (Root)
↑
Extension ClassLoader
↑
Application ClassLoader
↑
Custom ClassLoaders

1. Bootstrap ClassLoader

  • Parent: None (root classloader)
  • Implementation: Native implementation (not Java)
  • Responsibility: Loads core Java classes
public class BootstrapClassLoaderDemo {
public static void main(String[] args) {
// Classes loaded by Bootstrap ClassLoader
ClassLoader stringClassLoader = String.class.getClassLoader();
ClassLoader objectClassLoader = Object.class.getClassLoader();
System.out.println("String class ClassLoader: " + stringClassLoader); // null
System.out.println("Object class ClassLoader: " + objectClassLoader); // null
// Bootstrap classpath can be viewed using:
String bootClassPath = System.getProperty("sun.boot.class.path");
System.out.println("Bootstrap Classpath: " + bootClassPath);
}
}

Location: JRE/lib/rt.jar, JRE/lib/resources.jar, etc.

2. Extension ClassLoader

  • Parent: Bootstrap ClassLoader
  • Implementation: sun.misc.Launcher$ExtClassLoader
  • Responsibility: Loads extension libraries
public class ExtensionClassLoaderDemo {
public static void main(String[] args) {
// Get Extension ClassLoader
ClassLoader extensionClassLoader = ClassLoader.getSystemClassLoader().getParent();
System.out.println("Extension ClassLoader: " + extensionClassLoader);
System.out.println("Extension ClassLoader parent: " + extensionClassLoader.getParent());
// Extension directory
String extDirs = System.getProperty("java.ext.dirs");
System.out.println("Extension directories: " + extDirs);
}
}

Location: JRE/lib/ext/ directory

3. Application ClassLoader

  • Parent: Extension ClassLoader
  • Implementation: sun.misc.Launcher$AppClassLoader
  • Responsibility: Loads application classes from classpath
public class ApplicationClassLoaderDemo {
public static void main(String[] args) {
// Current class's ClassLoader (Application ClassLoader)
ClassLoader appClassLoader = ApplicationClassLoaderDemo.class.getClassLoader();
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println("Application ClassLoader: " + appClassLoader);
System.out.println("System ClassLoader: " + systemClassLoader);
System.out.println("Are they same? " + (appClassLoader == systemClassLoader));
// Parent hierarchy
System.out.println("Application → " + appClassLoader);
System.out.println("Parent → " + appClassLoader.getParent());
System.out.println("Parent's Parent → " + appClassLoader.getParent().getParent());
// Classpath
String classPath = System.getProperty("java.class.path");
System.out.println("Classpath: " + classPath);
}
}

Delegation Model in Action

public class ClassLoadingDemo {
public static void main(String[] args) {
// Demonstrate classloader hierarchy
printClassLoaderHierarchy(String.class);        // Bootstrap
printClassLoaderHierarchy(javax.swing.JFrame.class); // Extension? 
printClassLoaderHierarchy(ClassLoadingDemo.class);   // Application
// Load class explicitly
loadClassWithDetails("java.lang.String");
loadClassWithDetails("java.util.ArrayList");
loadClassWithDetails("com.mysql.jdbc.Driver"); // If in classpath
}
public static void printClassLoaderHierarchy(Class<?> clazz) {
System.out.println("\nClass: " + clazz.getName());
ClassLoader loader = clazz.getClassLoader();
while (loader != null) {
System.out.println("  ↳ " + loader);
loader = loader.getParent();
}
System.out.println("  ↳ Bootstrap ClassLoader");
}
public static void loadClassWithDetails(String className) {
try {
System.out.println("\nLoading: " + className);
Class<?> loadedClass = Class.forName(className);
System.out.println("Loaded by: " + loadedClass.getClassLoader());
} catch (ClassNotFoundException e) {
System.out.println("Class not found: " + className);
}
}
}

Custom ClassLoader Example

public class CustomClassLoader extends ClassLoader {
private final String classPath;
public CustomClassLoader(String classPath, ClassLoader parent) {
super(parent); // Set parent classloader
this.classPath = classPath;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
// Convert class name to file path
String path = name.replace('.', '/') + ".class";
File file = new File(classPath, path);
if (!file.exists()) {
throw new ClassNotFoundException("Class not found: " + name);
}
// Read class file bytes
byte[] classBytes = Files.readAllBytes(file.toPath());
// Define the class
return defineClass(name, classBytes, 0, classBytes.length);
} catch (IOException e) {
throw new ClassNotFoundException("Failed to load class: " + name, e);
}
}
}
// Using custom classloader
public class CustomClassLoaderDemo {
public static void main(String[] args) throws Exception {
String customClassPath = "/path/to/classes";
CustomClassLoader customLoader = new CustomClassLoader(
customClassPath, 
CustomClassLoaderDemo.class.getClassLoader()
);
// Load class using custom classloader
Class<?> customClass = customLoader.loadClass("com.example.MyClass");
Object instance = customClass.newInstance();
System.out.println("Class loaded by: " + customClass.getClassLoader());
System.out.println("Parent: " + customClass.getClassLoader().getParent());
}
}

ClassLoader Methods and Features

public class ClassLoaderMethodsDemo {
public static void main(String[] args) {
ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
// Important methods
System.out.println("Name: " + appClassLoader.getName());
System.out.println("Parent: " + appClassLoader.getParent());
// Get resource
java.net.URL resource = appClassLoader.getResource("application.properties");
System.out.println("Resource: " + resource);
// Get resources
try {
java.util.Enumeration<java.net.URL> resources = 
appClassLoader.getResources("META-INF/MANIFEST.MF");
while (resources.hasMoreElements()) {
System.out.println("Found: " + resources.nextElement());
}
} catch (IOException e) {
e.printStackTrace();
}
// Check if class is loaded
System.out.println("String class loaded: " + 
appClassLoader.getDefinedPackage("java.lang") != null);
}
}

Practical Example: Plugin System

// Plugin interface
public interface Plugin {
void execute();
String getName();
}
// Plugin manager using custom classloaders
public class PluginManager {
private final Map<String, Plugin> plugins = new HashMap<>();
private final String pluginDirectory;
public PluginManager(String pluginDirectory) {
this.pluginDirectory = pluginDirectory;
}
public void loadPlugin(String pluginName) throws Exception {
// Create isolated classloader for each plugin
URL[] urls = { new File(pluginDirectory).toURI().toURL() };
URLClassLoader pluginClassLoader = new URLClassLoader(
urls, 
getClass().getClassLoader() // Parent is application classloader
);
// Load plugin class
Class<?> pluginClass = pluginClassLoader.loadClass(pluginName);
Plugin plugin = (Plugin) pluginClass.newInstance();
plugins.put(plugin.getName(), plugin);
System.out.println("Loaded plugin: " + plugin.getName() + 
" with classloader: " + pluginClass.getClassLoader());
}
public void executePlugin(String name) {
Plugin plugin = plugins.get(name);
if (plugin != null) {
plugin.execute();
}
}
}

Key Points to Remember

  1. Delegation Model: Child classloaders delegate to parents first
  2. Uniqueness: Classes are identified by (fully qualified name, classloader)
  3. Visibility: Child classloaders can see parent-loaded classes, but not vice versa
  4. Isolation: Different classloaders can load different versions of same class
  5. No Unloading: Classes cannot be unloaded, but classloaders can be garbage collected

Common Issues and Solutions

public class ClassLoaderIssues {
// ClassCastException due to different classloaders
public static void demonstrateClassCastIssue() throws Exception {
URLClassLoader loader1 = new URLClassLoader(new URL[0]);
URLClassLoader loader2 = new URLClassLoader(new URL[0]);
Class<?> class1 = loader1.loadClass("java.lang.String");
Class<?> class2 = loader2.loadClass("java.lang.String");
// This would cause ClassCastException if not same classloader
System.out.println("Same class? " + (class1 == class2));
}
// Resource loading strategies
public static void loadResource() {
// Different ways to load resources
ClassLoader cl = ClassLoaderIssues.class.getClassLoader();
// 1. Using ClassLoader (absolute path)
InputStream is1 = cl.getResourceAsStream("com/example/config.properties");
// 2. Using Class (relative to class package)
InputStream is2 = ClassLoaderIssues.class.getResourceAsStream("config.properties");
// 3. Using system classloader
InputStream is3 = ClassLoader.getSystemResourceAsStream("config.properties");
}
}

This hierarchical classloading system provides security, isolation, and flexibility in Java applications, allowing different parts of an application to have different class loading policies and dependencies.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper