上篇疑问
JVM篇 之 垃圾收集器中最后留一了一个问题
为什么CSM不直接使用标记压缩算法?
主要原因是,因为CMS垃圾回收是和用户线程一起运行的,如果使用标记压缩算法的话,就会导致大量在使用中的对象在堆中寻找不到,所以无法使用此算法。
class装载验证流程
自底向上检查,自上向下尝试加载
加载
此阶段是装载类的第一个阶段 ,负责通过各种方式获取类的二进制流,转为方法区数据结构,并在JAVA堆中生成对应的java.lang.Class对象
连接
连接过程又分为三个步骤,验证、准备、解析
验证
验证目的是为了保证Class流的格式是正确的,一定类通过了字节码检查,并不代表它一定没有问题,但是如果一个类没有通过字节码查检,那它一定是有问题,不能运行的。
文件格式的验证
主要是保证输入的字符流能正确地解析并存储于方法区之内,格式上符合一个Java类型信息的要求
** 是否以0xCAFEBABE开头
** 版本号是否合理** 检查常量TAG标志是否是被支持的常量类型
…..
元数据验证
目的是对类的元数据信息进行语义校验,保证不存在不符合Java语言规范的元数据信息,如果文件格式校验阶段是对文件结构的校验,那么该了阶段就是对文件内容在使用上是否满足约定的检查,有些像IDEA编译器的类一部分检测内容
** 是否有父类,Object之外所有的类都应该有父类
** 这个类的父类是否被final修饰了?
** 如果当前类是非抽象类是否实现了所有的抽象方法?** 类中的字符、方法是否与父类产生了矛盾?
….
字节码验证
在前面两个阶段,对类的的结构和使用规范上进行了校验,那么该阶段主要是对类的方法体进行校验分析,保证被校验类的方法在运行时不会做出危害JVM安全的行为,
** 运行检查
** 栈数据类型和操作码数据参数吻合
** 跳转指令指定到合理的位置** 方法体中的类型转换是否有效
符号引用验证
前3步已经对类的内容进行了校验,该阶段的主要是对当前类自身以外的信息进行匹配性校验,目的是保证解析 动作能正常执行
** 符号引用中通过字符串描述的全限定名
是否能找到对应的类
** 符号引用的类、字段 、方法的访问性,是否可以被当前类访问
** 符号引用的类、字段、方法是否存在
准备
分配内存,并为类设置初始值 (方法区中)
- public static int v=1;
在准备阶段中,v会被设置为0
在初始化的中才会被设置为1 - 对于static final类型,在准备阶段就会被赋上正确的值,因为static final类型被认为是常量,要保证在以后用到的过程中已经被赋于正确的值
public static final int v=1;
解析
符号引用替换为直接引用
符号引用:以一组符号情迷描述所引用的目标,符号可以是任何形式的字面量,只要使用时,能无歧义地定位到目标即可。符号引用与JVM实现的内存布局无关,引用的目标并不一定已经加载到内存中。各JVM实现的内在布局可以是不相同,但是它们能接受的符号引用必须是一致的,因为符号引用的字面量形式明确定义在java虚拟机规范的Class文件格式中。比如 java.lang.Object, 存放在常量池中。
直接引用:是指直接指向常量池中目标的指针、相对偏移量或者是一个能够间接定位到目标的句柄。
为什么要将符号引用为直接引用?
因为符号引用只是一种表示方式,无法直接使用,所以在解析阶段会被替换为直接引用,在使用时候需要知道,该字符串具体在内在中的哪个地址。
解析动作的解析范围如下:类或接口的解析
字段解析
类方法解析
接口方法解析
初始化
在准备阶段,已经给变量赋于默认值,在初始化阶段主要是给类变量进行赋正确的值
执行类构造器
- 对static变量赋于正确的值
- static{} 静态代码块语句
此阶段的一些约定:- 子类的
调用前保证父类的 被调用 是线程安全的
- 子类的
思考:
NoSuchMethodError, NoSuchFieldError,IllegalAccessError等错误发生在哪个阶段?
验证阶段的符号引用验证