【JVM学习系列】ClassLoader(类加载器)

时间:2023-01-02 23:17:57

1. 为何需要ClassLoader?

应用如果使用的是编译型语言例如C或C++,它们最终会编译成针对平台的可执行文件,这个过程中代码编译成可执行文件的过程称之为链接,主要目的是将多个编译后的obj文件合并成一个可执行的文件.

然后,对于动态编译的语言(例如Java)情况就有所不同了,在Java环境中,编译器将代码编译成class文件,class文件将会保持不变,知道JVM需要将其加载到运行环境中,换句话说就是上述链接的过程是发生在JVM中的,而不是在编译过程中发生的.

当一个Java应用程序启动的时候,第一个被执行的class(应用的入口)是具有一个public static void main()的类,通俗一点就是main方法,这个class通常也会依赖其他的class,这时候class loader就会尝试加载所有可能引用到的class.

为了让大家了解一下这种递归类的加载过程,下面是一个简单的hello world程序

public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}

为了显示应用运行过程中class加载的过程,我们在执行的时候指定-verbose:class命令行选项,下面是执行的过程.

E:\temp\java\jvm
λ java -verbose:class HelloWorld
[Opened D:\jre1.8\lib\rt.jar]
[Loaded java.lang.Object from D:\jre1.8\lib\rt.jar]
[Loaded java.io.Serializable from D:\jre1.8\lib\rt.jar]
[Loaded java.lang.Comparable from D:\jre1.8\lib\rt.jar]
[Loaded java.lang.CharSequence from D:\jre1.8\lib\rt.jar]
[Loaded java.lang.String from D:\jre1.8\lib\rt.jar]
[Loaded java.lang.reflect.AnnotatedElement from D:\jre1.8\lib\rt.jar]
[Loaded java.lang.reflect.GenericDeclaration from D:\jre1.8\lib\rt.jar]
[Loaded java.lang.reflect.Type from D:\jre1.8\lib\rt.jar]
[Loaded java.lang.Class from D:\jre1.8\lib\rt.jar]
[Loaded java.lang.Cloneable from D:\jre1.8\lib\rt.jar]
[Loaded java.lang.ClassLoader from D:\jre1.8\lib\rt.jar]
[Loaded java.lang.System from D:\jre1.8\lib\rt.jar]
[Loaded java.lang.Throwable from D:\jre1.8\lib\rt.jar]
[Loaded java.lang.Error from D:\jre1.8\lib\rt.jar]
[Loaded java.lang.ThreadDeath from D:\jre1.8\lib\rt.jar]
[Loaded java.lang.Exception from D:\jre1.8\lib\rt.jar]
[Loaded java.lang.RuntimeException from D:\jre1.8\lib\rt.jar]
[Loaded java.lang.SecurityManager from D:\jre1.8\lib\rt.jar]
[Loaded java.security.ProtectionDomain from D:\jre1.8\lib\rt.jar]
[Loaded java.security.AccessControlContext from D:\jre1.8\lib\rt.jar]
[Loaded java.security.SecureClassLoader from D:\jre1.8\lib\rt.jar]
[Loaded java.lang.ReflectiveOperationException from D:\jre1.8\lib\rt.jar]
[Loaded java.lang.ClassNotFoundException from D:\jre1.8\lib\rt.jar]
[Loaded java.lang.LinkageError from D:\jre1.8\lib\rt.jar]
[Loaded java.lang.NoClassDefFoundError from D:\jre1.8\lib\rt.jar]
[Loaded java.lang.ClassCastException from D:\jre1.8\lib\rt.jar]
[Loaded java.lang.ArrayStoreException from D:\jre1.8\lib\rt.jar]
[Loaded java.lang.VirtualMachineError from D:\jre1.8\lib\rt.jar]
[Loaded java.lang.OutOfMemoryError from D:\jre1.8\lib\rt.jar]
[Loaded java.lang.*Error from D:\jre1.8\lib\rt.jar]
[Loaded java.lang.IllegalMonitorStateException from D:\jre1.8\lib\rt.jar]
[Loaded java.lang.ref.Reference from D:\jre1.8\lib\rt.jar]
. //省略
. //省略
. //省略
[Loaded java.security.BasicPermissionCollection from D:\jre1.8\lib\rt.jar]
[Loaded HelloWorld from file:/E:/temp/java/jvm/]
[Loaded sun.launcher.LauncherHelper$FXHelper from D:\jre1.8\lib\rt.jar]
[Loaded java.lang.Class$
MethodArray from D:\jre1.8\lib\rt.jar]
[Loaded java.lang.Void from D:\jre1.8\lib\rt.jar]
Hello World!
[Loaded java.lang.Shutdown from D:\jre1.8\lib\rt.jar]
[Loaded java.lang.Shutdown$Lock from D:\jre1.8\lib\rt.jar]

大家可以通过上面的输出可以了解到应用执行中需要的class都在执行前加载到JVM中.

2. Java2平台中的类加载器

【JVM学习系列】ClassLoader(类加载器)
从JDK1.2开发,JVM中内置一个叫做bootstrap的类加载器,这个类加载器负责加载Java运行环境中的类,这个类加载器只负责加载boot classpath中的类,这些类是授信的类,具体点就是将在JRE目录中lib/rt.jar这个jar,这个过程不需要进行

除了bootstrap类加载器,JVM还有一个Extension类加载器,这个类加载器主要加载标准拓展API中的类,具体就是JRE目录中/lib/ext目录中的类或者是系统变量java.ext.dirs所配置的目录中的类.

最后一个就是系统类加载器,主要负责加载应用的类,通常你在执行java应用时,通过-classpath-cp选项指定的classpath中的类也会通过这个类进行加载的.

3. 类加载器的委派模型

在JVM中,类加载器是以树形结构组织的,每个类加载都会存在一个父类加载器(bootstrap类加载器除外,因为它是整个树的根)

类加载器的委派模型主要就是当一个类加载器加载一个类的时候,首先会请求父加载器是否已经加载了该类,父加载器如果没有找到该类,递归向其父加载器请求,如果这个过程中找到请求的类,就返回对应的类,如果这个递归查询过程中都没有该类的定义,则当前的类加载器会去加载该类.

大家可以通过默认的ClassLoader.loadClass的实现方法窥探其中:

protected synchronized Class<?> loadClass(String name, boolean resolve) {
// First, check if this class loader has directly defined the class or if the
// JVM has initiated the class load with this class loader.
Class<?> result = findLoadedClass(name);
if (result == null) {
try {
// Next, delegate to the parent.
result = getParent().loadClass(name);
} catch (ClassNotFoundException ex) {
// Finally, search locally if the parent could not find the class.
result = findClass(ex);
}
}
// As a remnant of J2SE 1.0.2, link the class if a subclass of the class
// loader class requested it (the JVM never calls the method,
// loadClass(String) passes false, and the protected access modifier prevents
// callers from passing true).
if (resolve) {
resolveClass(result);
}
return result;
}

参考:

  1. http://www.javaworld.com/article/2077260/learn-java/learn-java-the-basics-of-java-class-loaders.html
  2. http://www.journaldev.com/349/java-classloader
  3. http://*.com/questions/2424604/what-is-a-java-classloader
  4. http://javarevisited.blogspot.jp/2012/12/how-classloader-works-in-java.html
  5. http://tutorials.jenkov.com/java-reflection/dynamic-class-loading-reloading.html
  6. http://*.com/questions/2642606/java-classloader-delegation-model