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