27 类的生命周期

jvm是不会读.java文件的,需要编译成.class文件才行

  1. 加载

    1. 通过全类名获取此类的二进制字节流
    2. 将字节流所代表的静态存储结构转换成方法区的运行时数据结构
    3. 在内存中生成一个class对象,作为方法区这些数据的访问入口

    数组类型是由java虚拟机直接创建的,不是通过类加载器创建的。
    非数组类型可以通过自定义类加载器完成加载。(重写loadclass方法)

  2. 连接

    加载阶段未结束,连接阶段可能已经开始了。

    • 验证

      • 文件格式验证
        验证字节流是否符合class文件格式规范

        • 是否以0xCAFEBABE开头
        • 主次版本号是否在当前虚拟机处理范围之内
        • 常量池中的常量是否有不被支持的类型
      • 元数据验证
        对字节码的信息进行语义分析,保证描述的信息符合java
        语言规范

        • 类是否有父类
        • 是否继承了不能被继承的类(被final修饰的类)
      • 字节码验证

        • 验证程序语义合法
      • 符号引用验证
    • 准备

      为类变量(即静态变量)分配内存并设置变量初始值。
      在1.7前放在永久代方法区,1.7后字符串常量池、静态变量移动到堆中。
      这里的初始值是数据类型对应的0值。如果被final修饰了,则被赋值为实际值。

    • 解析

      将常量池内的“符号引用”替换为“直接引用”。
      针对类、接口、类方法、接口方法、方法类型、方法句柄、和调用限定符。
      直接引用是指向目标的指针、相对偏移量或可间接定位到目标的句柄。每个类有一张方法表存放类中所有方法,只要知道某方法在表中偏移量就可以直接调用。

  3. 初始化

    这一步jvm开始真正执行类中定义的java程序代码。
    执行方法,带锁线程安全,可能引起阻塞。
    以下五种情况,必须对类进行初始化

    • new、getstatic、putstatic、invokestatic指令,分别是创建实例、访问静态变量、给静态变量赋值、调用类静态方法。
    • 使用java.lang.reflect包对类进行反射调用时,例如class.forname(""),newInstance()
    • 初始化类时,如果父类没初始化,要先初始化父类
    • 虚拟机启动时,会先初始化main方法所在的类
    • 要使用轻量级的反射调用methodHandle、VarHandle,必须先使用findStaticVarHandle初始化类
    • 如果接口定义了jdk8新加入的默认方法(被default修饰的方法),初始化这个接口的实现类之前要先初始化接口。
  4. 使用

  5. 卸载

    卸载即class对象被gc,需要满足三个要求

    1. 所有实例对象都被gc了。
    2. 类没有被任何地方引用。
    3. 类加载器实例被gc。
      由jvm自带的类加载器加载的类是不会被卸载的,由我们自定义的类加载器加载的类是可能被卸载的。

你可能感兴趣的:(java)