类加载器子系统
类加载器子系统指jvm系统中负责查找和加载类型的子系统。
jvm有两种类加载器:启动类加载器和用户定义类加载器。
前者是jvm实现的一部分,后者是java应用的一部分。
由不同的类加载器加载的类被放在了jvm的不同名字空间中。
类加载器子系统包括了jvm的其他几个部分,利用了
java.lang库中的几个类。例如,用户定义类加载器继承
了类java.lang.ClassLoader。ClassLoader的方法
为应用提供了访问jvm类加载器子系统的接口。另外,对
每个加载上来的类,类加载器都会创建一个java.lang.Class
的实例来描述这个类。象其他所有对象一样用户定义类加载器和Class的实例也放在堆中。加载的类型信息放在方法区。
加载,链接 和初始化
类加载器子系统除了定位和加载二进制类文件外,还必须保证类文件的正确性,为类变量分配内存并初始化和帮助对符号引用的解析。这些行为严格遵照以下的顺序:
加载:包括定位和加载某类型的二进制数据
链接:包括进行验证,准备和解析(可选)
a 验证:确信引入类型的正确性
b 准备:为类变量分配内存并初始化
c 解析:将符号引用改为直接引用
初始化:激活类变量的初始化java代码
启动类加载器
jvm必须能够识别和加载放在符合类文件格式的二进制文件中的类和接口.除了类文件,jvm实现也可以识别其他文件。
启动类加载器对每个jvm实现都是必须的,它知道如何加载包括java api的可信类。jvm规范并没有定义启动类如何定位类文件,这个将由jvm实现者自行掌握。
启动类加载器必须能够根据一个完整的有效的类型名称产生这个类型的定义数据。在Windows98平台上采用Sun's 1.1 jdk的jvm实现提供了一个常用的方法。启动类加载器通过对定义在环境变量classpath中的所有路径进行搜寻,直到找到了这个类定义文件,类定义文件的名称是类名加".class"。通常一个类都有一个所属包,启动类加载器会到相应的子目录下去找,例如一个叫做java.lang.Object的类,启动类加载器会在所有定义在CLASSPATH的路径中的javalang目录下找一个叫做Object.class的类,直到找到。
在1.2的版本中,启动类加载器只在系统类的安装目录中查找,而
不是所有定义在classpath的目录。这个任务交给了
系统类加载器,系统类加载器是一个用户定义的类加载器,它在
jvm启动时就被自动创建。
用户定义的类加载器
尽管用户定义类加载器本事是属于java应用的,但可以通过它
的四个函数与jvm交互。
// 在类java.lang.ClassLoader中声明的四个函数:
protected final Class defineClass(
String name,
byte data[],
int offset,
int length);
protected final Class defineClass(
String name,
byte data[],
int offset,
int length,
ProtectionDomain protectionDomain);
protected final Class findSystemClass(String name);
protected final void resolveClass(Class c);
任何jvm的实现都必须把这四个函数连到内部的类加载器子系统。
两个defineClass()方法都有一个类型为字节数组名称为
data[]的输入参数,defineClass()希望data[offset]到
data[offset +length]中的数据是一个合法的类型数据,这
个新类型将会被放到指定的保护区中,如果是第一个defineClass
方法则会放到缺省保护区中,每个jvm实现必须保证defineClass
能够把新类型放到方法区中。
findSystemClass()方法以一个类型名为参数。在1.0和
1.1版本,这个方法会通过启动类加载器加载这个类型。假如
类加载器已经加载了这个类型,它就会返回描述这个类型的
Class对象。如果没有找到,就会抛出一个
ClassNotFoundException.在1.2版本,
findSystemClass()方法通过系统类加载器来加载这个类型。
不管通过那个类加载器,其对外的行为是被规范的。
resolveClass()方法接收一个对Class对象的引用。它对这个
Class对象代表的类型进行链接。
名字空间
每个类加载器都有自己的名字空间,所以一个java应用可以对
同一个类型加载多次。这样一个类型的完整的有效名称并不足以
确定在jvm中的唯一性。当有多个类加载器都加载了同一个类型,
为了唯一确定类型还要在类型名称前加上类加载器的名字。
除了加载类型的数据外,jvm还要记录类型的加载器。
当jvm解析一个类到另一个类的符号引用时,jvm会
保证被引用类的类加载器与引用类的类加载器相同。