JVM篇之类加载

上篇疑问

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等错误发生在哪个阶段?

验证阶段的符号引用验证

BK wechat
扫一扫,用手机访问本站
---------------- 本文结束 ----------------