什么是类加载器
?
与普通程序不同的是,
Java
程序(
class
文件)并不是本地的可执行程序。当运行
Java
程序时,首先运行
JVM
(
Java
虚拟机),然后再把
Java class
加载到
JVM
里头运行,负责加载
Java class
的这部分就叫做
Class
Loader
。
JVM
本身包含了一个
ClassLoader
称为
Bootstrap ClassLoader
,和
JVM
一样,
BootstrapClassLoader
是
用本地代码实现的,
它负责加载核心
JavaClass
(
即所有
java.*
开头的类)
。
另外
JVM
还会提供两个
ClassLoader
,
它们都是用
Java
语言编写的,由
BootstrapClassLoader
加载;其中
Extension ClassLoader
负责加载扩展的
Javaclass
(例如
所有
javax.*
开头的类
和
存放在
JRE
的
ext
目录下的类
),
ApplicationClassLoader
负责加载应
用程序自身的类。
当运行一个程序的时候,
JVM
启动,运行
bootstrapclassloader
,该
ClassLoader
加载
java
核心
API
(
ExtClassLoader
和
AppClassLoader
也在此时被加载),然后调用
ExtClassLoader
加载扩展
API
,最后
AppClassLoader
加载
CLASSPATH
目录下定义的
Class
,这就是一个程序最基本的加载流程。
注
:
学
ClassLoader
看
OSGI
什么时候加载类
?
什么时候
JVM
会使用
ClassLoader
加载一个类呢?当你使用
java
去执行一个类,
JVM
使用
ApplicationClassLoader
加载这个类;
然后如果类
A
引用了类
B
,
不管是直接引用还是用
Class.forName()
引用,
JVM
就会找到加载类
A
的
ClassLoader
,
并用这个
ClassLoader
来加载类
B
。
JVM
按照运行时的有效执行语句,
来决定是否需要装载新类,从而装载尽可能少的类,这一点和编译类是不相同的。
Why use your own ClassLoader?
似乎
JVM
自身的
ClassLoader
已经足够了,为什么我们还需要创建自己的
ClassLoader
呢?
因为
JVM
自带的
ClassLoader
只是懂得从本地文件系统加载标准的
java class
文件,如果编写你自己的
ClassLoader
,你可以做到:
1
)在执行非置信代码之前,自动验证数字签名
2
)动态地创建符合用户特定需要的定制化构建类
3
)从特定的场所取得
java class
,例如数据库中
4)
等等
事实上当使用
Applet
的时候,就用到了特定的
ClassLoader
,因为这时需要从网络上加载
java class
,并且
要检查相关的安全信息。
目前的应用服务器大都使用了
ClassLoader
技术,即使你不需要创建自己的
ClassLoader
,了解其原理也有
助于更好地部署自己的应用。
类加载器的树状结构
&
委托代理模式
当你决定创建你自己的
ClassLoader
时,需要继承
java.lang.ClassLoader
或者它的子类。在实例化每个
ClassLoader
对象时,需要指定一个父对象;如果没有指定的话,系统自动指定
ClassLoader.getSystemClassLoader()
为父对象。
所以当创建自己的
Class Loader
时,只需要重载
findClass()
这个方法。
卸载
?
重载
?
当一个
javaclass
被加载到
JVM
之后,
它有没有可能被卸载呢?我们知道
Win32
有
FreeLibrary()
函数,
Posix
有
dlclose()
函数可以被调用来卸载指定的动态连接库,
但是
Java
并没有提供一个
UnloadClass()
的方法来卸载指
定的类。
在
Java
中,
java class
的卸载仅仅是一种对系统的优化,
有助于减少应用对内存的占用。
既然是一种优化方
法,那么就完全是
JVM
自行决定如何实现,对
Java
开发人员来说是完全透明的。
在什么时候一个
java class/interface
会被卸载呢?
Sun
公司的原话是这么说的:
"class or interfacemay be
unloaded if and only if its class loader is unreachable. Classesloaded by the bootstrap loader may not be
unloaded."
事实上我们关心的不是如何卸载类的,我们关心的是如何更新已经被加载了的类从而更新应用的功能。
JSP
则是一个非常典型的例子,如果一个
JSP
文件被更改了,应用服务器则需要把更改后的
JSP
重新编译,然后加
载新生成的类来响应后继的请求。
其实一个已经加载的类是无法被更新的,
如果你试图用同一个
ClassLoader
再次加载同一个类,
就会得到异
常(
java.lang.LinkageError: duplicate classdefinition
),我们只能够重新创建一个新的
ClassLoader
实例来再次
加载新类。至于原来已经加载的类,开发人员不必去管它,因为它可能还有实例正在被使用,只要相关的实例都
被内存回收了,那么
JVM
就会在适当的时候把不会再使用的类卸载。
使用线程上下文类加载器
,
可以在执行线程中
,
抛弃双亲委派加载链模式
,
使用线程上下文里的类加载器加
载类
.
典型的例子有
,
通过线程上下文来加载第三方库
jndi
实现
,
而不依赖于双亲委派
.
大部分
java app
服务器
(jboss, tomcat..)
也是采用
contextClassLoader
来处理
web
服务。
当然
,
好东西都有利弊
.
使用线程上下文加载类
,
也要注意
,
保证多根需要通信的线程间的类加载器应该是
同一个
,
防止因为不同的类加载器
,
导致类型转换异常
(ClassCastException).
|
|