The Java Classloader, a part of the Java Runtime Environment, dynamically loads Java classes into the Java Virtual Machine. Each Java class must be loaded by a class loader.
When the JVM is started, three class loaders are used:
- Bootstrap class loader
- Extensions class loader
- System class loader
The bootstrap class loader loads the core Java libraries located in the <JAVA_HOME>/jre/lib
directory. This class loader, which is part of the core JVM, is written in native code.
The extensions class loader loads the code in the extensions directories (<JAVA_HOME>/jre/lib/ext
, or any other directory specified by the java.ext.dirs system property). It is implemented by the sun.misc.Launcher$ExtClassLoader
class.
The system class loader loads code found on java.class.path
, which maps to the CLASSPATH
environment variable. This is implemented by the sun.misc.Launcher$AppClassLoader
class.
The following picture lists for conclusion.
We can verify that with code:
public class JavaTestApp {
public static void main(String[] args) {
try {
Class<?> clazz = Class.forName("com.qiniu.fusion.app.Demo");
ClassLoader classLoader = clazz.getClassLoader();
System.out.printf("currentClassLoader is %s\n", classLoader.getClass().getSimpleName());
while (classLoader.getParent() != null) {
classLoader = classLoader.getParent();
System.out.printf("Parent is %s\n", classLoader.getClass().getSimpleName());
}
} catch (Exception e) {
System.out.println(e);
}
}
}
class Demo {
static int count = 0;
}
The output is,
currentClassLoader is AppClassLoader
Parent is ExtClassLoader
The parent of AppClassLoader
is ExtClassLoader
, while ExtClassLoader.getParent() = null
. BootstrapClassLoader
is built-in of JVM.
Every Class
object has a ClassLoader
Every Class
object contains a getClassLoader()
reference to the ClassLoader
that defined it.
For example, this snippet of code outputs sun.misc.Launcher$AppClassLoader@42a57993
public class AppTest {
public static void main(String[] args) {
Demo d = new Demo();
System.out.println(d.getClass().getClassLoader());
}
}
class Demo {
static int count = 0;
}
It arouses my interest to see what getClassLoader()
does.
// Class.java
public ClassLoader getClassLoader() {
ClassLoader cl = getClassLoader0();
if (cl == null)
return null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
ClassLoader.checkClassLoaderPermission(cl, Reflection.getCallerClass());
}
return cl;
}
ClassLoader getClassLoader0() { return classLoader; }
private final ClassLoader classLoader;
private Class(ClassLoader loader) {
classLoader = loader;
}
Each Class
object includes a private field classLoader
, which is initialized in JVM not by private constructor.
Parent Delegation Model
Allow me back to ClassLoader
.
// ClassLoader.java
public abstract class ClassLoader {
// The parent class loader for delegation
private final ClassLoader parent;
// The classes loaded by this class loader. The only purpose of this table
// is to keep the classes from being GC'ed until the loader is GC'ed.
private final Vector<Class<?>> classes = new Vector<>();
}
The ClassLoader
class uses a delegation model to search for classes and resources. Each instance of ClassLoader
has an associated parent class loader.
Let’s see how a Class
object is loaded.
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
When requested to find a class or resource,
- First, check if the class has already been loaded.
- if
parent
exists, it will load class. otherwise, The bootstrap class loader tries load the class. - If still not found, then invoke findClass in order to find the class.
loadClass
determines the class(es) referred to, since the methods and constructors of objects created by a class loader may reference other classes.
defineClass
Normally, the Java virtual machine loads classes from the local file system in a platform-dependent manner. For example, on UNIX systems, the virtual machine loads classes from the directory defined by the CLASSPATH
environment variable.
However, some classes may not originate from a file; they may originate from other sources, such as the network, or they could be constructed by an application. The method defineClass(String, byte[], int, int)
converts an array of bytes into an instance of class Class
.
Instances of this newly defined class can be created using Class#newInstance
.
// ClassLoader.java
protected final Class<?> defineClass(String name, byte[] b, int off, int len,
ProtectionDomain protectionDomain)
throws ClassFormatError
{
protectionDomain = preDefineClass(name, protectionDomain);
String source = defineClassSourceLocation(protectionDomain);
Class<?> c = defineClass1(name, b, off, len, protectionDomain, source);
postDefineClass(c, protectionDomain);
return c;
}
preDefineClass
determines protection domain, and check that:
- not define
java.*
class, - signer of this class matches signers for the rest of the classes in package.
Example
An example is necessary to illustrate the Parent Delegation Model
.
Compile Demo
class to jar, and move it to $JAVA_HOME/jre/lib/ext/
.
javac classloader/Demo.java
jar cvf Demo.jar classloader/Demo.class
mv Demo.jar $JAVA_HOME/jre/lib/ext/
Then, loads Demo
class with explicit AppClassLoader
.
public class JavaTestApp {
public static void main(String[] args) {
try {
ClassLoader appClassLoader = ClassLoader.getSystemClassLoader(); // AppClassLoader
Class<?> clazz = appClassLoader.loadClass("com.qiniu.fusion.app.Demo");
ClassLoader classLoader = clazz.getClassLoader();
System.out.printf("ClassLoader is %s", classLoader.getClass().getSimpleName());
} catch (Exception e) {
System.out.println(e);
}
}
}
However, the outputs, currentClassLoader is ExtClassLoader
, means Demo
class is loaded with ExtClassLoader
still.
Reference