Java虚拟机探险之Class Loader

Posted on 2010-08-17 10:47 天快黑了 阅读(2209) 评论(0)  编辑  收藏 所属分类: JVM
 

众所周知,所有的Java class文件都是由JVM(虚拟机)加载并执行的。深入理解JVM对于我们提高Java技术和解决Java问题都有非常大的帮助。

JVM内部主要包括内存管理和Class Loader(类加载器)两个部分。熟悉了内存管理,我们就会清楚程序在内存中是怎么分配和执行的,就能解决所有和对象相关的问题(比如Memory Leak)。理解了Class Loader,就能解决所有类找不到(比如遇到NoClassDefFoundErrorClassNotFoundException)或配置文件找不到问题。

这次我们只讨论JVMClass Loader,下次再讨论JVM的内存管理。

Class Loader的主要作用就是负责查找类并将其加载到内存中。有趣的是,Java中的Class Loader也是由Java所写,就和普通的class一样。这就产生了一个是鸡生蛋还是蛋生鸡的问题,到底第一个class由谁来加载呢?我们稍后会来讨论这个问题。

先来看一下Class Loader所具有的特点。

1.       继承关系

虽然Class Loader也是一个Java class,但这里的继承不是指定义class时使用的extends关键字来实现的继承,而是指由属性来维持的继承关系。即通过Class Loader的构造方法或其它方法显式的设置一个父Class Loader

2.       代理关系

每一个Class Loader在接到请求去加载一个类之前(默认,访问一个类的时候,就会由加载当前类的Class Loader去加载被访问的类),它会首先请求它的父Class Loader来尝试加载,依次往上,如果父Class Loader加载成功,则直接返回,子Class Loader不再查找。

否则依次往下查找并加载。如果直到被请求的Class Loader也没有找到要加载的类,则会出现NoClassDefFoundErrorClassNotFoundException

当然如果被请求的类已经加载到了内存中,就不会触发这个查找过程了,而是直接返回已经加载的类。

我们来看一个例子,假设有图1中的Class Loader层次:


如果我们请求
Class Loader E去加载Test.class,首先它会请求父Class Loader D去尝试加载。同样Class Loader D会先请求它的父Class Loader C去尝试加载Test.class。当然这里Class Loader C找不到Test.class,于是转回由Class Loader D去加载。最终Class Loader D成功找到了D:"Test.class,并将其加载到内存中。

同样,如果我们请求Class Loader F去加载Test3.classClass Loader CClass Loader D在各自的搜索范围内都找不到Test3.class。最终会由Class Loader F自己加载F:"Test3.class到内存中。

如果我们请求Class Loader D去加载Test3.class,最终就会出现NoClassDefFoundErrorClassNotFoundException

3.       同一继承链可见性

在同一个Class Loader对象的继承链中,下面被加载的类可以访问上面被加载的类,反之则不可以。

同样以图1为例,Test4.class可以访问到D:"Test.classC:"Test2.class

而如果D:"Test.classC:"Test2.class尝试访问Test4.class,就会出现NoClassDefFoundErrorClassNotFoundException

4.       多个继承链不可见性

多个继承链之间彼此看不到对方,不能相互访问。

还以图1为例,如果Test4.class访问Test3.class,或反过来Test3.class访问Test4.class,都会引起NoClassDefFoundErrorClassNotFoundException

理解了Class Loader所具有的特点,我们来看看JDK中都预置了哪些Class Loader。也是JVM启动时默认创建的Class Loader。如图2


通过图
2,我们可以看到Bootstrap Class LoaderJVM中的祖先Class Loader。它是JDK中唯一一个由C++所写的Class Loader,它负责加载JDK的核心类库(rt.jar)以及另外两个由Java所写的Class LoaderExt Class LoaderApp Class Loader)。之后就功成身退,转由Ext Class LoaderApp Class Loader加载所有应用中用到的类。

一般我们的应用都是通过设置CLASSPATH,最终由App Class Loader来加载。根据Class Loader的继承关系,我们应用中的类可以访问JDK的核心类库。反之则会出错。

我们再来看看WebLogicClass Loader的层次关系(如图4)。如果需要,大家可以参考一下WebLogicWAREAR的文件结构(如图3




WLS
中自定义了很多新的Class Loader,当然他们的祖先Class Loader都是JDK中的App (or System) Class Loader。我们来看一下每个Class Loader的职责。

1.       JDK App (or System) Class Loader

l       负责加载WLS启动脚本中CLASSPATH中设置的类

l       所有的类都会最先由它尝试加载

l       因为CLASSPATH的值在运行期不允许修改,所以由该Class Loader加载的类在运行期不能被动态卸载(替换)

2.       EJB Class Loader (1)

l       负责加载单独的EJB jar里的类。

l       不同的EJB jar文件会被不同实例的Class Loader加载,因此EJB jar彼此之间互相看不到对方

3.       WAR Class Loader (1)

l       负责加载单独的WAR里的类

l       不同的WAR文件会被不同实例的Class Loader加载,因此WAR彼此之间互相看不到对方

4.       EAR Class Loader

l       负责加载EAR里面的APP-INF下的类

l       不同的EAR文件会被不同实例的EAR Class Loader加载,因此EAR彼此之间互相看不到对方

l       它下面有一个EJB Class Loader (2) 实例,负责加载EAR里面所有的EJB jar。因此,EAR中的EJB彼此之间可以看到对方

l       EJB Class Loader (2) 下有多个WAR Class Loader (2) 实例。每个实例负责加载EAR里面的一个WAR。所以,EAR中的WAR彼此之间看不到对方

l       根据继承链规则,WAR可以看到所有的EJBAPP-INF下的所有类。 EJB可以看到APP-INF下的所有类,但反之则不可以

Class Loader虽然称为类加载器,但并不意味着只能用来加载Class,我们还可以利用它来查找图片和配置文件等资源。比如,我们经常使用getClass().getResourceAsStream(name)来查找配置文件。同样,查找其它资源文件的方式和上面一样,也会先请求父Class Loader来负责查找。

这里,我们只简单介绍了Class Loader对于类的查找,而关于Class Loader的具体加载、校验和初始化的过程,感兴趣的朋友可以参考《深入Java虚拟机》


只有注册用户登录后才能发表评论。


网站导航:
 

posts - 5, comments - 25, trackbacks - 0, articles - 1

Copyright © 天快黑了