深入Java虚拟机读书笔记[6:7]

第六章 Java class文件

这章的内容讲的是编译后的class文件格式,我根据内容写了个class文件解析程序。 https://github.com/JohnWong/class-file-parser

第七章 类型的生命周期

1. Java虚拟机通过装载、连接与初始化一个Java类型

连接步骤包括验证、准备、解析(可选)。在类和接口被装载和连接的时机上,Java虚拟机规范给实现提供了一定的灵活性。但是要求每个类或者接口必须在首次主动使用时初始化。主动使用包括:

a) 创建某个类的实例。
b) 调用某个类的静态方法。
c) 使用某个类或接口的静态字段,或者对其赋值。final修饰的静态字段除外。
d) 调用Java API中的某些反射方法。
e) 初始化某个类的子类。
f) 虚拟机启动某个被标明为启动类的类。

2. 装载

a) 通过该类型的全限定名产生一个代表该类型的二进制数据流 b) 解析这个二进制数据流为方法区内的内部数据结构 c) 创建一个表示该类型的java.lang.Class类的实例

类装载器并不需要一直等到某个类型首次主动使用时再去装入他。Java虚拟机规范允许类装载器缓存 Java类型的二进制表现形式。如果预先装载时遇到问题,应该在首次主动使用时报告该问题。

3. 验证

连接过程的第一步,确认类型符合Java语言的语义并且不会危害虚拟机的完整性。不同虚拟机验证有一些灵活性,可以决定如何以及何时验证类型。除Object外所有类都有超类和class文件格式检查发生在装载期间,但是逻辑上属于验证。检查被装载的类型是否有任何问题的整个过程都属于验证。符号引用验证属于验证,但是往往在解析的时候发生,是连接的第三步。正式验证阶段执行之前未进行的和之后不会进行的检查。包括:

a) 类之间二进制兼容性检查:final不能有子类、final方法不能被覆盖、类型和超类型之间无不兼容的方法声明。
b) 类与所有超类之间互相二进制兼容:常量池入口互相之间一致、常量池中所有特殊字符串是否符合格式、字节码完整性。

虚拟机的实现没有强求在正式的连接验证阶段进行字节码验证,但是连接过程中一次性验证字节码流而非在程序执行时动态验证,使得Java程序运行速度得到很大提高。

4. 准备

连接过程的第二步,为类变量分配内存,设置默认初始值。但在初始化阶段之前,类变量都没有被初始化为真正的初始值(不执行Java代码)。也可能为一些数据结构分配内存,目的是提高程序的性能。例如方法表,包含指向类中每一个方法的指针,使得继承方法执行时不需要搜索超类。

5. 初始化

一个变量的初始值通过类变量初始化语句或者静态初始化语句给出。这些被Java编译器收集在一起,放到类/接口初始化方法中。在class文件中为<clinit>。只能由拟机调用。

初始化一个类的步骤:

a) 如果类存在直接超类且未被初始化,则初始化直接超类。
b) 如果类存在一个类初始化方法,就执行此方法。

使用一个非常量静态字段只有当类或者接口的确声明了这个字段时才主动使用。如果一个字段既是static又是final的,并且使用一个编译时常量表达式初始化,就不是对声明该字段的类的主动使用。

6. 对象的声明周期

类实例化方法:

a) new
b) Class或者java.lang.reflect.Constructor对象的newInstance方法
c) 现有对象的clone方法
d) java.io.ObjectInputStream类的readObject方法反序列化

还有几种情况会隐含地实例化:

a) 每个命令行参数中都会有String对象,组织成String数组传递给main方法
b) 对于Java虚拟机中装载的每一个类都会暗中实例化一个Class对象来代表这个类型
c) Java虚拟机装载常量池中的包含CONSTANT_String_ info入口的类的时候,会创建新的String对象的实例来表示这些常量字符串。把方法区的CONSTANT_String_ info入口转换成一个堆中的String实例的过程是常量解析过程的一部分。
d) 使用字符串连接操作符会在计算表达式的过程中创建String和StringBuffer对象。

类实例初始化方法为<init>,如果没有构造方法,会默认创建一个无参构造方法,并调用父类的无参构造方法。

7. finalize()方法会在垃圾收集器释放这个实例所占据的内存空间之前执行。如果终结方法代码执行后,对象重新被引用了,随后再次变得不被引用,垃圾收集器不会第二次调用终结方法。

8. 卸载类型

使用启动类装载器装载的类型永远不会被卸载。只有用户定义的类装载器装载的类型才会变得不可触及,被虚拟机回收。判断是否可以触及:a) 如果程序保持对Class实例的明确引用;b) 在堆中存在一个可触及的对象,在方法区中它的类型数据指向一个Class实例。

Comments

comments powered by Disqus