类的生命周期详解(一)

类的加载:

  • 类的加载的最终产品是位于堆区中的Class对象
  • Class对象封装了类在方法区内的数据结构,并且向JAVA程序员提供了访问方法区内的数据结构的接口
  • 类加载器的种类:
    1. java虚拟机自带的加载器
      (1)根类加载器(Bootstrap),由C++编写,无法在java代码中获取该类
      (2)扩展类加载器(Extension),使用Java代码实现
      (3)系统(或者应用)类加载器(System),使用Java代码实现
    2. 用户自定义的类加载器
      (1)java.lang.ClassLoader的子类
      (2)用户可以定义类的加载方式
      3.类加载器并不需要等到某个类被“首次使用”时再加载它:
      (1) jvm规范允许类加载在预料某个类将要被使用时就预先加载它,如果在预先加载的过程中遇到了.Class文件缺失或者存在错误,类加载器必须在程序首次主动使用该类时才报告错误(LinkageError错误)。
      (2) 如果一个类一直没有被程序主动使用,那么类加载器就不会报告错误
      (3) 类被加载后,就进入连接阶段。连接就是将已经读入到内存的类的二进制数据合并到虚拟机的运行时环境中去。

类的验证:

  • 类文件的结构检查:确保类文件遵从java文件的固定格式
  • 语义检查:确保类本身符合java语言的语法规定,比如校验final类型的类没有子类以及final类型的方法没有被覆盖(可防止恶意用户的恶意操作)
  • 字节码验证:确保字节码流可以被java虚拟机安全地执行。字节码流代表java方法(包括静态方法和实例方法),他是由被称做操作码的单字节指令组成的序列,每一个操作码后都跟着一个或者多个操作数。字节码验证步骤会检查每个操作码是否合法,即是否有着合法的操作数。
  • 二进制兼容性的验证:确保相互引用的类之间协调一致。例如在Worker类的goWork()方法中会调用Car类的run()方法。java虚拟机在验证Worker类时,会检查方法在方法区内是否存在Car类的run()方法,假如不存在(当Worker类和Car类的版本不兼容,就会出现这种问题,就会抛出NoSuchMethodError错误)

类的准备:

在准备阶段,java虚拟机为类的静态变量分配内存,并设置默认的初始值。例如对于以下Simple类,在准备阶段,将int类型的静态变量a分配4个字节的内存空间,并且赋予默认值0。为long类型的静态变量b分配8个字节的内存空间,并且赋予默认值0

public class Simple {

    private static int a=1;
    public static long b;
    static {
        b=2;
    }
}

类的解析阶段:

在解析阶段,java虚拟机会把类的二进制数据中的符号引用替换为直接引用,例如在Worker类的goWork()方法中会引用Car类的run()方法.

 public void goWork(){
        ca.run();//表示符号引用
    }

Worker类的二进制数据中包含了一个对Carrun()方法的符号引用,它由run()方法的全类名和相关描述符组成。在解析阶段,虚拟机会把这个符号引用直接替换为一个指针,该指针指向Car类的run()方法在方法区内的内存位置,这个指针就是直接引用。

类的初始化:

1.在初始化阶段,java虚拟机执行类的初始化语句,为类的静态变量赋予初始值。在程序中,静态变量初始化有两种途径:

  • 在静态变量的声明处进行初始化
  • 在静态代码块中进行初始化。
    例如在以下代码中,静态变量ab都被显式初始化,而c没有被显式初始化,它将默认保持默认值0
public class Simple {

    private static int a=1;
    public static long b;
    public static long c;
    static {
        b=2;
    }

}

2.类的初始化的步骤:

  • 假如这个类还没有加载和连接,那就先进行加载和连接
  • 假如类存在直接的父类,并且这个父类还没有被初始化,那就先初始化直接的父类
  • 假如类中存在直接的初始化语句,那就依次执行这些初始化语句。

3.类的初始化的时机:
当java虚拟机初始化一个类时,要求它的所有父类都已经被初始化,但是这条规则并不适用于接口。

  • 在初始化一个类时,并不会先初始化它所实现的接口
  • 在初始化一个接口时,并不会先初始化它的父接口
    因此,一个父接口并不会因为它的子接口或者实现类的初始化而初始化,只有当程序首次使用特定接口的静态变量时,才会导致接口的初始化。

使用阶段:

在类的使用过程中依然存在三步:对象实例化、垃圾收集、对象终结。

  • 对象实例化:就是执行类中构造函数的内容,如果该类存在父类JVM会通过显示或者隐示的方式先执行父类的构造函数,在堆内存中为父类的实例变量开辟空间,并赋予默认的初始值,然后在根据构造函数的代码内容将真正的值赋予实例变量本身,然后,引用变量获取对象的首地址,通过操作对象来调用实例变量和方法 。
  • 垃圾收集:当对象不再被引用的时候,就会被虚拟机标上特别的垃圾记号,在堆中等待GC回收
  • 对象的终结:对象被GC回收后,对象就不再存在,对象的生命也就走到了尽头

卸载阶段:

即类的生命周期走到了最后一步,程序中不再有该类的引用,该类也就会被JVM执行垃圾回收,从此生命结束…
在这里插入图片描述

发布了41 篇原创文章 · 获赞 14 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Yunwei_Zheng/article/details/104018013