Java类加载器 Java三个类加载器及它们的相互关系
cswhl 人气:0一、什么是类加载器?
虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块称为“类加载器”
类加载器可以说是Java语言的一项创新,也是Java语言流行的重要原因之一,它最初是为了满足Java Applet的需求而开发出来的。虽然目前Java Applet技术基本上已经“死掉”,但类加载器却在类层次划分、OSGi、热部署、代码加密等领域大放异彩,成为了Java技术体系中一块重要的基石,可谓是失之桑榆,收之东隅。
类加载器虽然只用于实现类的加载动作,但它在Java程序中起到的作用却远远不限于类加载阶段。对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性,每一个类,都拥有一个独立的类名称空间。这句话可以表达得更通俗一些:比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义。否则,即使这两个类来源于同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相等。
二、AppClassLoader系统类加载器
AppClassLoader应用类加载器,又称为系统类加载器,负责在JVM启动时,加载来自命令java中的classpath或者java.class.path系统属性或者CLASSPATH操作系统属性所指定的JAR类包和类路径
- 获取当前类的类加载器:
public class AppClassLoaderTest { public static void main(String[] args) { System.out.println(ClassLoader.getSystemClassLoader()); } }
输出:
sun.misc.Launcher$AppClassLoader@73d16e93
这说明AppclassLoader是当前应用classpath所有类的加载器。
查看ClassLoader的源码可发现:在没有特定说明的情况下,用户自定义的任何类加载器都将该类加载器作为自定义类加载器的父加载器.
- 通过执行下面的代码即可获得classpath的加载路径:
String classPath = System.getProperty("java.class.path"); for (String path : classPath.split(";")) { System.out.println(path); }
输出:
.
通常是当前执行字节码的路径。
- main函数的类的加载就是使用AppClassLoader加载器进行加载的,而AppClassLoader的父加载器是ExtClassLoader:
public class AppClassLoaderTest { public static void main(String[] args) { ClassLoader classLoader = Test.class.getClassLoader(); System.out.println(classLoader); System.out.println(classLoader.getParent()); } private static class Test { } }
输出:
sun.misc.Launcher$AppClassLoader@73d16e93
sun.misc.Launcher$ExtClassLoader@15db9742
三、ExtClassLoader扩展类加载器
ExtClassLoader称为扩展类加载器,主要负责加载Java的扩展类库,默认加载JAVA_HOME/jre/lib/ext/目录下的所有jar包或者由java.ext.dirs系统属性指定的jar包.放入这个目录下的jar包对AppClassLoader加载器都是可见的(因为ExtClassLoader是AppClassLoader的父加载器,并且Java类加载器采用了委托机制).
- ExtClassLoader的类扫描路径通过执行下面代码来看一下:
public class ExtClassLoaderTest { public static void main(String[] args) { String extDirs = System.getProperty("java.ext.dirs"); for (String path : extDirs.split(";")) { System.out.println(path); } } }
执行结果如下:
D:\TOOLS\JAVA\lib\ext
C:\WINDOWS\Sun\Java\lib\ext
- 从上面的路径中随意选择一个类,来看看它的类加载器是什么:
public class ExtClassLoaderTest { public static void main(String[] args) { ClassLoader classLoader = sun.security.ec.SunEC.class.getClassLoader(); System.out.println(classLoader); System.out.println(classLoader.getParent()); } }
输出:
sun.misc.Launcher$ExtClassLoader@30f39991
null
从输出结果可知ExtClassLoader的父加载器为null
四、BootstrapClassLoader启动类加载器
启动类加载器,是Java类加载层次中最顶层的类加载器,负责加载JDK中的核心类库,如:rt.jar、resources.jar、charsets.jar等
- 通过如下程序获得该类加载器从哪些地方加载了相关的jar或class文件:
import java.net.URL; public class BootstraplassLoaderPath { public static void main(String[] args) { URL[] urLs = sun.misc.Launcher.getBootstrapClassPath().getURLs(); for (URL url : urLs) { System.out.println(url.toExternalForm()); } } }
输出:
file:/D:/TOOLS/JAVA/lib/resources.jar
file:/D:/TOOLS/JAVA/lib/rt.jar
file:/D:/TOOLS/JAVA/lib/sunrsasign.jar
file:/D:/TOOLS/JAVA/lib/jsse.jar
file:/D:/TOOLS/JAVA/lib/jce.jar
file:/D:/TOOLS/JAVA/lib/charsets.jar
file:/D:/TOOLS/JAVA/lib/jfr.jar
file:/D:/TOOLS/JAVA/classes
- 从rt.jar中选择String类,看一下String类的类加载器是什么:
public class stringBootstraplassLoaderTest { public static void main(String[] args) { ClassLoader classLoader = String.class.getClassLoader(); System.out.println(classLoader); } }
输出:
null
由于BootstrapClassLoader对Java不可见,所以返回了null,我们也可以通过某一个类的加载器是否为null来作为判断该类是不是使用BootstrapClassLoader进行加载的依据。
另外上面提到ExtClassLoader的父加载器返回的是null,那是否说明ExtClassLoader的父加载器是BootstrapClassLoader.
Bootstrap ClassLoader是由C/C++编写的,它本身是虚拟机的一部分,所以它并不是一个JAVA类,也就是无法在java代码中获取它的引用,JVM启动时通过Bootstrap类加载器加载rt.jar等核心jar包中的class文件,之前的int.class,String.class都是由它加载。
五、加载器关系总结
JVM初始化sun.misc.Launcher并创建Extension ClassLoader和AppClassLoader实例。并将ExtClassLoader设置为AppClassLoader的父加载器,Bootstrap则是ExtClassLoader的父加载器.
关系图如下:
六、参考资料
加载全部内容