1 Java 类加载器结构
动态的类加载是JVM 的一个重要特征, 它为Java 平台提供了在
运行时间安装软件组件的能力。动态类加载有以下几个特性:首先是
惰性加载,即系统要求解析链接类时才加载类,这种加载方式只运行
一次。另外,Java 平台支持用户可定义的类加载策略,使用户定义的类
加载器可以定制类发现的方式以及在加载特定代码时给类赋予安全
属性。最后,动态类加载支持多个命名空间的理念。例如,浏览器可以
用不同的类加载器加载各个不同网页中的applet, 这样在applet 类之
间可以保持一定程度的隔离。
JVM 中类加载器有三种: 引导类加载器Bootstrap ClassLoader、扩
展类加载器ExtClassLoader 和应用类加载器App ClassLoader。其中,
BootstrapClassLoader 是JVM 实现的一部分,系统中唯一的、用编写虚
拟机的语言编写的类加载器。BootstrapClassLoader 用默认方式从
classpath 中加载Java 运行环境提供的所有核心类, 它只加载
jre\classes 中的核心类。这些类是所有应用程序必须的,因此不是“即
用即装”,而是首先装入并永驻JVM,直至JVM 退出。ExtClassLoader
和App ClassLoader 类加载器主要负责加载核心类之外的类。Ext
ClassLoader 和AppClassLoader 由JVM 创建并继承自ClassLoader 且由
Java 语言实现, 也被称为用户类加载器。Ext ClassLoader 加载
jre\lib\ext 中所有类,此目录下是Java 运行环境所需要的扩展类。App
ClassLoader 加载classpath 指定的路径中用户应用程序所需要的类。
2 类加载器原理分析
JVM 加载本地class 文件的工作被分配到多个类加载器中, 引导
类加载器加载核心Java API 的class 文件, 用户自定义类加载器负责
加载其它class 文件。例如,应用程序运行的class 文件、用于安装或下
载标准扩展的class 文件、类路径中发现的类库中的class 文件等。
Java 虚拟机开始运行时,在应用程序启动前,至少创建一个自定义类
加载器。Java 类加载器之间有一定的继承关系, 扩展类加载器
ExtClassLoader 的父加载器是引导类加载器Bootstrap ClassLoader,应
用类加载器AppClassLoader 的父加载器是扩展类加载器。JVM 在加载
类时默认采用的是双亲委托机制。通俗的讲,就是某个特定的类加载
器在接到加载类的请求时,首先将加载任务委托给父类加载器,请求
“双亲”来加载这个类型,“双亲”再请求它自己的“双亲”来加载这个类
型,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只
有父类加载器无法完成此加载任务时,才自己试着去加载。
在图1“双亲委托模型”中,这条链的顶
端是引导类加载器,末端是用户自定义类加
载器。当用户请求加载一个类时,首先自底
向上(右侧带虚线箭头所示),逐一检查此类
加载器是否成功加载所请求的类,若已成功
加载了类,则直接返回类的引用实例。若直
到引导类加载器也未成功加载类,则自顶向
下(左侧实线箭头所示),逐一尝试加载类,
直到用户自定义类加载器。若用户自定义类
加载器不能成功加载类则抛出异常
ClassNotFoundException。除了引导类加载器
外的其他类加载器有且只有一个双亲,若没
有显式地指定双亲,系统则根据情况为它们分配一个默认的双亲。
3 类加载委托机制示例
下面我们通过一个例子验证类加载中双亲委托机制的实现。首
先,写一个简单的java 程序如下:
public class Demo{
public static void main (String [] args) {
System.out.println ( " loaded by " +Demo.class.getClassLoader ().
getClass()) ;
System.out.println ("ClassLoader Demo.") ;
}
}
输出结果如下:loadded by class sun.misc.launcher$AppClassLoader
ClassLoader Demo.
很显然,Demo 是定义的一个类,Demo.class.getClassLoader () 返回
的就是应用类加载器,Demo.class.getClassLoader().getClass() 的结果是
class sun.misc.Lancher$AppClassLoader。可以看出这是SUN 的一个
基础实现,是一个内部类。
然后, 将当前目录下的类文件Demo.class 打包到文件test.jar,将
文件test.jar 放到< Java jdk 安装目录>\jre\lib\ext 目录下。再运行以上
的代码,结果如下:
loadded by class sun.misc.launcher$ExtClassLoader
ClassLoader Demo.
和前面的实验结果对比,我们明显可以验证前面说的双亲委托机
制,应用类加载器在接到加载Demo 类的请求时,首先将请求委托给
父类加载器(扩展类加载器),扩展类加载器抢先完成了加载请求。 |
|