- 字节码验证。 类装入器对于类的字节码要做许多检测,以确保格式正确、行为正确。
- 类准备。 这个阶段准备代表每个类中定义的字段、方法和实现接口所必需的数据结构。
- 解析。 在这个阶段,类装入器装入类所引用的其他所有类。可以用许多方式引用类:
在初始化 阶段,类中包含的静态初始化器都被执行。在这一阶段末尾,静态字段被初始化成默认值。
在这三个阶段末尾,类被完整地装入,可以使用了。请注意可以用惰性方式执行类装入,所以类装入过程的某些部分可能在第一次使用类的时候才执行,而不是在装入时执行。
显式装入与隐式装入
类装入的方式有两种 —— 显式 或 隐式,两者之间有些细微差异。
显式 类装入发生在使用以下方法调用装入的类的时候:
cl.loadClass()(cl 是 java.lang.ClassLoader 的实例)
Class.forName()(启动的类装入器是当前类定义的类装入器)
当调用其中一个方法的时候,指定的类(以类名为参数)由类装入器装入。如果类已经装入,那么只是返回一个引用;否则,装入器会通过委托模型装入类。
隐式 类装入发生在由于引用、实例化或继承导致装入类的时候(不是通过显式方法调用)。在每种情况下,装入都是在幕后启动的,JVM 会解析必要的引用并装入类。与显式类装入一样,如果类已经装入了,那么只是返回一个引用;否则,装入器会通过委托模型装入类。
类的装入通常组合了显式和隐式类装入。例如,类装入器可能先显式地装入一个类,然后再隐式地装入它引用的所有类。
JVM 的调试特性
前面一节介绍了类装入的基本原则。这一节介绍 IBM JVM 中内置的帮助调试的特性。其他 JVM 也有类似的调试特性;请参阅相关文档来了解细节。
详细输出
可以用 -verbose 命令行选项打开 IBM JVM 的详细输出。当某些事件发生的时候(例如,类装入时),详细输出会在控制台上显示信息。要想得到额外的类装入信息,可以用详细类输出。可以用 -verbose:class 选项启动这个模式。
解释详细输出
详细输出列出已经打开的所有 JAR 文件,包括到这些 JAR 的完整路径。下面是一个示例:
...
[Opened D:\jre\lib\core.jar in 10 ms]
[Opened D:\jre\lib\graphics.jar in 10 ms]
...
|
所有装入的类都已经列出,同时还指出它们是从哪个 JAR 文件或目录装入的。例如:
...
[Loaded java.lang.NoClassDefFoundError from D:\jre\lib\core.jar]
[Loaded java.lang.Class from D:\jre\lib\core.jar]
[Loaded java.lang.Object from D:\jre\lib\core.jar]
...
|
详细类输出显示额外信息,例如在装入超类的时候,在运行静态初始化器的时候。下面是一些示例输出:
...
[Loaded HelloWorld from file:/C:/myclasses/]
[Loading superclass and interfaces of HelloWorld]
[Loaded HelloWorldInterface from file:/C:/myclasses/]
[Loading superclass and interfaces of HelloWorldInterface]
[Preparing HelloWorldInterface]
[Preparing HelloWorld]
[Initializing HelloWorld]
[Running static initializer for HelloWorld]
... |
详细输出还显示一些内部抛出的异常(如果发生的话),包含堆栈跟踪。
用 -verbose 解决问题
详细输出有助于解决类路径问题,例如没有打开 JAR 文件(因此不在类路径中)或从错误的位置装入了类。
IBM 详细类装入
知道类装入器在哪里寻找类、特定的类是由哪个类装入器装入的,通常很有用。可以用 IBM 详细类装入命令行选项得到这个信息:-Dibm.cl.verbose=<class name>。可以用正则表达式声明类的名称,例如 Hello* 会跟踪所有以 Hello 开头的名称。
这个选项也可用于用户定义的类装入器,只要它们直接或间接地扩展了 java.net.URLClassLoader。
解释 IBM 详细类装入的输出
IBM 详细类装入的输出显示了要装入指定类的类装入器以及它们查找的位置。例如,假设用以下命令行:
java -Dibm.cl.verbose=ClassToTrace MainClass
|
在这里,MainClass 在它的主方法中引用了 ClassToTrace。这会形成像 这里 一样的输出。
在列出类装入器的时候,父类在子女之前列出,因为标准的委托模型的工作方式是父类优先。
请注意,引导类装入器没有输出。只有扩展了 java.net.URLClassLoader 的类装入器才有输出。还请注意,类装入器列在它们的类名之下;如果类装入器有两个实例,那么可能无法区分它们。