Java 类装入原理

一、类装入基础
存在三种类装入器,分别装入三种不同应用类
引导类装入器
扩展类装入器
系统类装入器
类装入器首先判断要求它装入的类是否与过去装入的类相同。如果相同,就返回上次返回的类(即保存在缓存中的类)。如果不是,就把装入类的机会交给父类。这两步递归地以深度优先的方式重复。如果父类返回 null(或抛出 ClassNotFoundException),那么类装入器会在自己的路径中寻找类的源。
因为父类类装入器总是先得到装入类的机会,所以类装入器装入的类最靠近根。这意味着所有核心引导类都是由引导装入器装入的,这就保证装入了类(例如 java.lang.Object)的正确版本。这也可以让类装入器看到自己或父类或祖先装入的类,但是不能看到子女装入的类。
图 1 显示了三个标准的类装入器:

图 1. 类装入器委托模型

引导类装入器(也称作基本(primordial) 类装入器)不能由 Java 代码实例化。(通常是因为它是作为 VM 本身的一部分实现的。)这个类装入器可以从启动的类路径装入核心系统类,通常是位于 jre/lib 目录的 JAR 文件。但是能用 -Xbootclasspath 命令行选项修改这个类路径。
扩展(extension) 类装入器(也称作标准扩展 类装入器)是引导类装入器的一个孩子。它的主要职责是从扩展目录装入类,通常位于 jre/lib/ext 目录。这提供了简单地访问新扩展的能力,例如不同的安全扩展,不需要修改用户的类路径即可实现。
系统(system) 类装入器(也称作应用程序 类装入器)负责从 CLASSPATH 环境变量指定的路径装入代码。默认情况下,这个类装入器是用户创建的任何类装入器的父类。这也是 ClassLoader.getSystemClassLoader() 方法返回的类装入器。

二、类装入阶段
类的装入实际上可以分成三个阶段:装入、链接和初始化
图 2 显示了这三个阶段:

图 2. 类装入的阶段

装入 阶段包括:找到必要的类(通过查找每个类路径)并装入字节码。在 JVM 中,装入阶段为类对象提供了非常基本的内存结构。在这一阶段不处理方法、字段和引用的其他类。所以,类还不能使用。
链接 是三个阶段中最复杂的一个。可以把它分成三个主要阶段:
• 字节码验证:类装入器对于类的字节码要做许多检测,以确保格式正确、行为正确。
• 类准备:这个阶段准备代表每个类中定义的字段、方法和实现接口所必需的数据结构。
• 解析: 在这个阶段,类装入器装入类所引用的其他所有类。可以用许多方式引用类:
o 超类
o 接口
o 字段
o 方法签名
o 方法中使用的本地变量
初始化 阶段,类中包含的静态初始化器都被执行。在这一阶段末尾,静态字段被初始化成默认值。
在这三个阶段末尾,类被完整地装入,可以使用了。请注意可以用惰性方式执行类装入,所以类装入过程的某些部分可能在第一次使用类的时候才执行,而不是在装入时执行。
显式装入与隐式装入
类装入的方式有两种 —— 显式 或 隐式,两者之间有些细微差异。
显式 类装入发生在使用以下方法调用装入的类的时候:
• cl.loadClass()(cl 是 java.lang.ClassLoader 的实例)
• Class.forName()(启动的类装入器是当前类定义的类装入器)
当调用其中一个方法的时候,指定的类(以类名为参数)由类装入器装入。如果类已经装入,那么只是返回一个引用;否则,装入器会通过委托模型装入类。
隐式 类装入发生在由于引用、实例化或继承导致装入类的时候(不是通过显式方法调用)。在每种情况下,装入都是在幕后启动的,JVM 会解析必要的引用并装入类。与显式类装入一样,如果类已经装入了,那么只是返回一个引用;否则,装入器会通过委托模型装入类。
类的装入通常组合了显式和隐式类装入。例如,类装入器可能先显式地装入一个类,然后再隐式地装入它引用的所有类。

三、查看装入类信息
详细输出
可以用 -verbose 命令行选项打开 JVM 的详细输出。当某些事件发生的时候(例如,类装入时),详细输出会在控制台上显示信息。要想得到额外的类装入信息,可以用详细类输出。可以用 -verbose:class 选项启动这个模式。
解释详细输出
详细输出列出已经打开的所有 JAR 文件,包括到这些 JAR 的完整路径。下面是一个示例:
...
[Opened D:\jre\lib\core.jar]
[Opened D:\jre\lib\graphics.ja]
...

所有装入的类都已经列出,同时还指出它们是从哪个 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 文件(因此不在类路径中)或从错误的位置装入了类。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值