Java 类的生命周期-笔记

类的生命周期

创建进程-创建虚拟机实例->> {加载类-【验证-准备-解析】-初始化-实例化-【使用实例-gc工作】- 卸载类}

加载

类的加载一般用虚拟机自带类加载器来加载,也可以自定义加载器 ClassLoader。Java中的类加载器采用双亲委托机制,也就是先看这个类是否有父类,有则只需加载父类。此处的双亲可以理解为包含关系,并不是单单的继承关系。
静态常量赋值(编译期)->加载开始->静态代码块->静态变量

连接

连接包含以下三个阶段:

  • 验证:类的正确性,比如语法是否正确。具体包括 文件格式验证、元数据验证、字节码验证和符号引用验证。

  • 准备:为静态变量(非静态常量和非普通变量)分配内存,并设置一个默认值(由虚拟机设置的值,不是用户写死的值,一般基本数据类型默认赋值为 0,引用数据类型默认赋值为 null );静态常量直接赋值为用户写死的值,静态常量的值存储在虚拟机内存的常量区中,在不用加载类的情况下就可以直接访问。

    1
    2
    public static final int AGE = 5; // AGE 是静态常量,在类不用加载就可以访问 AGE,因为在编译期就已经初始化了。
    public static final int AGE = new Randon(10).nextInt(); // AGE 是静态常量,但是存在 new 关键字,对象的创建分配实在程序运行期,所以 new 关键字会触发该类的加载和后续的类初始化。
  • 解析:将类中的符号引用转换为直接引用(例子:根据身份号(符号引用)来查找具体的住址(直接引用))

初始化

给静态变量赋值(用户设置的值),并不是所有的类都会自动初始化的,比如一个类里面空空如也,这初始化它干嘛呢对吧?
自动初始化也叫主动初始化,符合以下情况的类会自动初始化:

  1. 该类中有 new 关键字、反射、序列化
  2. 该类中调用了别的类的静态方法
  3. 该类存在 main 方法
  4. 对别的类或接口中的静态字段赋值(final 修饰的静态字段除外)
  5. 该类中有调用 java api 中的反射方法
  6. 初始化该类的子类
    7.在虚拟机启动时,将该类标记为启动类

内存分配

  • 方法区:存储类的二进制数据,其中方法区中分有一个常量区,专门存放常量和符号引用的信息。

  • 堆区:存储对象实例,当类的二进制数据在方法区存放好了之后,会创建该类的一个 Class 对象,然后将该对象存放在堆区,方便共享调用,该对象包含该类在方法区内数据结构,通过该 Class 对象我们可以利用 java 代码调用该类在方法区内的数据接口,像反射调用。

  • 栈区:先进先出,方法的调用会创建一个调用栈,在栈顶会有一个独立的栈帧。主要存放每个方法的局部变量、方法参数、编译的临时变量和方法出口等信息。该区域容量小访问速度快,每个方法是一个栈帧,当放执行完成后,改栈帧会被清除;如果方法中调用了其他方法,则继续在该栈顶创建新的栈桢。

帧栈

每一次函数的调用,都会在调用栈(call stack)上维护一个独立的栈帧(stack frame)
一个帧栈包含两个指针:帧指针(ebp 寄存器)和栈指针(esp 寄存器),ebp 指向当前栈帧的底部,esp 指向栈帧的顶部。

类的卸载(类生命结束)

  1. 执行 System.exit()
  2. 程序执行完成(Android App 除外)
  3. 异常导致的退出终止
  4. 虚拟机进程终止

类加载器的种类

  1. 启动 类加载器
  2. 扩展 类加载器
  3. 应用程序(系统) 类加载器
  4. 自定义 类加载器