定义
程式编制一般需经编辑、编译、连线、载入和运行几个步骤。在我们的套用中,有一些公共代码是需要反覆使用,就把这些代码编译为“库”档案;在连线步骤中,连线器将从库档案取得所需的代码,複製到生成的执行档中。这种库称为静态库,其特点是执行档中包含了库代码的一份完整拷贝;缺点就是被多次使用就会有多份冗余拷贝。为了克服这个缺点可以採用动态连线库。这个时候连线器仅仅是在执行档中打上标誌,说明需要使用哪些动态连线库;当运行程式时,载入器根据这些标誌把所需的动态连线库载入到记忆体。
另外在当前的编程环境中,一般都提供方法让程式在运行的时候把某个特定的动态连线库载入并运行,也可以将其卸载(例如Win32的LoadLibrary()&FreeLibrary()和Posix的dlopen()&dlclose())。这个功能被广泛地用于在程式运行时刻更新某些功能模组或者是程式外观。
介绍
与普通程式不同的是,Java程式(class档案)并不是本地的可执行程式。当运行Java程式时,首先运行JVM(Java虚拟机),然后再把Java class载入到JVM里头运行,负责载入Java class的这部分就叫做Class Loader。
JVM本身包含了一个ClassLoader称为Bootstrap ClassLoader,和JVM一样,Bootstrap ClassLoader是用本地代码实现的,它负责载入核心Java Class(即所有java.*开头的类)。另外JVM还会提供两个ClassLoader,它们都是用Java语言编写的,由Bootstrap ClassLoader载入;其中Extension ClassLoader负责载入扩展的Java class(例如所有javax.*开头的类和存放在JRE的ext目录下的类),Application ClassLoader负责载入应用程式自身的类。
装载类
什么时候JVM会使用ClassLoader载入一个类呢?当你使用java去执行一个类,JVM使用Application ClassLoader载入这个类;然后如果类A引用了类B,不管是直接引用还是用Class.forName()引用,JVM就会找到载入类A的ClassLoader,并用这个ClassLoader来载入类B。
原因
似乎JVM自身的ClassLoader已经足够了,为什么我们还需要创建自己的ClassLoader呢?
因为JVM自带的ClassLoader只是懂得从本地档案系统载入标準的java class档案,如果编写你自己的ClassLoader,你可以做到:
1)在执行非置信代码之前,自动验证数字签名
2)动态地创建符合用户特定需要的定製化构建类
3)从特定的场所取得java class,例如资料库中
4) 等等
事实上当使用Applet的时候,就用到了特定的ClassLoader,因为这时需要从网路上载入java class,并且要检查相关的安全信息。
如今的套用伺服器大都使用了ClassLoader技术,即使你不需要创建自己的ClassLoader,了解其原理也有助于更好地部署自己的套用。
ClassLoader Tree & Delegation Model
当你决定创建你自己的ClassLoader时,需要继承java.lang.ClassLoader或者它的子类。在实例化每个ClassLoader对象时,需要指定一个父对象;如果没有指定的话,系统自动指定ClassLoader.getSystemClassLoader()为父对象。如下图:
在Java 1.2后,java class的载入採用所谓的委託模式(Delegation Modle),当调用一个ClassLoader.loadClass()载入一个类的时候,将遵循以下的步骤:
1)检查这个类是否已经被载入进来了?
2)如果还没有载入,调用父对象载入该类
3)如果父对象无法载入,调用本对象的findClass()取得这个类。
所以当创建自己的Class Loader时,只需要重载findClass()这个方法。
卸载和载入
当一个java class被载入到JVM之后,它有没有可能被卸载呢?我们知道Win32有FreeLibrary()函式,Posix有dlclose()函式可以被调用来卸载指定的动态连线库,但是Java并没有提供一个UnloadClass()的方法来卸载指定的类。
在Java中,java class的卸载仅仅是一种对系统的最佳化,有助于减少套用对记忆体的占用。既然是一种最佳化方法,那么就完全是JVM自行决定如何实现,对Java开发人员来说是完全透明的。
在什么时候一个java class/interface会被卸载呢?Sun公司的原话是这么说的:class or interface may be unloaded if and only if its class loader is unreachable. Classes loaded by the bootstrap loader may not be unloaded.
事实上我们关心的不是如何卸载类的,我们关心的是如何更新已经被载入了的类从而更新套用的功能。JSP则是一个非常典型的例子,如果一个JSP档案被更改了,套用伺服器则需要把更改后的JSP重新编译,然后载入新生成的类来回响后继的请求。
其实一个已经载入的类是无法被更新的,如果你试图用同一个ClassLoader再次载入同一个类,就会得到异常(java.lang.LinkageError: duplicate class definition),我们只能够重新创建一个新的ClassLoader实例来再次载入新类。至于原来已经载入的类,开发人员不必去管它,因为它可能还有实例正在被使用,只要相关的实例都被记忆体回收了,那么JVM就会在适当的时候把不会再使用的类卸载。
类装载器例子
BeanVetoableChange t = (BeanVetoableChange) Beans.instantiate(ClassLoader.getSystemClassLoader(), BeanVetoableChange);













