当前位置: 首页 > news >正文

【从零学习JVM|第三篇】类的生命周期(高频面试题)

前言:

在Java编程中,类的生命周期是指类从被加载到内存中开始,到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期,让读者对此有深刻印象。

目录

​编辑前言:

类的生命周期

类的加载阶段

核心任务

连接阶段

 验证(Verification)

 准备(Preparation)

解析(Resolution)

初始化阶段

() 方法特性

不会导致初始化

初始化触发条件(严格规定)

使用阶段

卸载阶段

面试题

总结


类的生命周期

一个类的生命周期分为五个阶段,分别是加载,连接,初始化,使用和卸载。

类的加载阶段

加载是将类的字节码文件(.class)加载到 JVM 内存的过程,类加载器会通过不同的形式,去得到字节码文件的内容,然后通过JVM,把内容加载到方法区和堆区,而我们开发者只能访问到堆区的内容,方法区我们是访问不到的,堆区的内容是字节码文件的核心内容。

核心任务

  1. 查找字节码

    • 通过类的全限定名查找字节码文件

    • 搜索路径:类路径(classpath)、JAR 文件、网络资源等

  2. 创建类结构

    • 解析字节码并创建对应的类数据结构

    • 在方法区(元空间)存储类的运行时常量池、字段和方法信息

  3. 创建 Class 对象

    • 在堆内存中创建 java.lang.Class 对象

    • 该对象作为访问类元数据的入口

连接阶段

在加载阶段结束后进入连接阶段,连接阶段分为三个子阶段,确保类字节码的正确性和可用性

 验证(Verification)

确保字节码符合 JVM 规范和安全要求:

  • 文件格式验证:魔数(0xCAFEBABE)、版本号等

  • 元数据验证:语义检查(是否有父类、final类是否被继承等)

  • 字节码验证:数据流和控制流分析

  • 符号引用验证:检查引用的类、字段和方法是否存在

 准备(Preparation)

为类变量分配内存并设置初始值:

  • 静态变量分配

    • 在方法区分配内存

    • 设置类型默认值(零值)

      • int → 0

      • boolean → false

      • 引用类型 → null

  • 特殊处理

    • static final 常量:直接赋程序指定值

// 准备阶段直接赋值
public static final int MAX = 100;

解析(Resolution)

将符号引用转换为直接引用:

  • 符号引用:用一组符号描述引用的目标

  • 直接引用:指向目标的指针、偏移量或句柄

初始化阶段

执行静态代码块的内容和给静态变量赋值,以字节码文件视角就是执行<clinit>方法

<clinit>() 方法特性

  1. 自动生成

    • 编译器收集所有类变量赋值静态代码块

    • 按源代码顺序合并生成

  2. 线程安全

    • JVM 保证只有一个线程执行初始化

    • 其他线程会阻塞等待完成

  3. 父类优先

    • 执行子类的 <clinit>() 前

    • 必须先执行父类的 <clinit>()

不会导致初始化

  1. 无静态代码块且无静态变量赋值语句。
  2. 有静态变量的声明,但是没有赋值语句。
  3. 静态变量的定义使用final关键字,这类变量会在准备阶段直接初始化 。
  4. 直接访问父类的静态变量,不会触发子类的初始化。
  5. 数组的创建不会导致数组中元素的类进行初始化。

初始化触发条件(严格规定)

  1. new 实例化对象

  2. 访问类的静态变量(非 final 常量)

  3. 调用类的静态方法

  4. 反射调用(Class.forName())

  5. 初始化子类时(父类需先初始化)

  6. JVM 启动时的主类

  7. MethodHandle 解析结果涉及静态方法

使用阶段

类的初始化之后,它就可以在程序中自由使用了。这包括创建实例、调用方法和访问字段等操作。在这个阶段,对象会被创建和操作,它们各自也会经历自己的生命周期。

卸载阶段

 在某些情况下,当一个类不再需要时,它会被卸载。类的卸载发生在垃圾收集的过程中,当确定某个类的Class对象不再被引用,且对应的ClassLoader实例也不再存在时,JVM就可能卸载这个类。但是,在常见的Java应用中,由于系统类加载器加载的类一直会被引用,所以这些类通常只有在JVM停止运行时才会被卸载。

面试题

说出一下代码的运行结果:

public class Demo01 {public static void main(String[] args) {System.out.println(B02.a);}
}
class A02
{static int a=0;static{System.out.println("A02");a=1;}
}
class B02 extends A02{static{System.out.println("B02");a=2;}
}

运行结果为A02 1  原因是因为:访问父类的静态变量,只初始化父类

public class Demo01 {public static void main(String[] args) {new B02();System.out.println(B02.a);}
}
class A02
{static int a=0;static{System.out.println("A02");a=1;}
}
class B02 extends A02{static{System.out.println("B02");a=2;}
}

首先执行main方法,new了B02所以要初始化它,但是它是子类,所以要先初始化A02,首先给赋值为0然后打印出A02,在给a赋值1,在初始化B02,打印出B02,a赋值为2,然后打印出a的值2

运行结果是:A02 B02 2

public class test5 {public static void main(String[] args) {System.out.println("A");new test5();new test5();}public  test5(){System.out.println("B");}{System.out.println("C");}static {System.out.println("D");}}
  1. 先执行test5这个类的静态代码块输出D
  2. 在执行main方法打印A
  3. 在初始化test5,执行构造器方法和代码块方法,但是代码块先执行所以第一个new执行CB
  4. 第二个new执行CB
  5. 结果:DACBCB

以上面试题你都做对了吗?

总结

    Java类的生命周期包括:加载(将字节码加载到内存,生成Class对象)、连接(验证字节码、准备静态变量内存并赋默认值、解析符号引用)、初始化(执行静态代码块和赋值)、使用(创建实例、调用方法)、卸载(类不再使用时被回收)。按此顺序完成从加载到销毁的完整过程。

学习类的生命周期,能让我们对java语言有一个更深的认识,也能让我们在面试中多一点机会。

总之,类的生命周期从加载到卸载,经历了多个阶段,每个阶段都有特定的任务和目标。理解类的生命周期有助于我们更好地理解和管理Java程序的运行机制。

感谢你的阅读,希望文章能给你带来帮助,你的阅读点赞就是我最大的动力。

相关文章:

  • 【JVM】- 内存结构
  • 八股文——JVM
  • 电子电气架构 --- E/E架构战略
  • 【RAG排序】rag排序代码示例-简单版
  • 医疗风险预测AI模型:机器学习与深度学习方法的深度分析与实践
  • Linux应用开发之网络套接字编程(实例篇)
  • 中医有效性探讨
  • 安卓贝利自动点击器高级版下载安装教程
  • 第二届计算机视觉、机器人与自动化工程国际学术会议(CRAE 2025)
  • Easy Rules规则引擎:轻量级Java规则处理实践指南
  • Vue3 + TypeScript + Element Plus 设置表单中日期控件的宽度
  • Linux 性能利器:详解 `top` 命令的使用与输出信息解析
  • 信号的诞生:Linux进程信号的启示与奥秘
  • 【AI News | 20250609】每日AI进展
  • 第二十三课:手搓随机森林
  • aurora与pcie的数据高速传输
  • Kafka入门-Broker以及文件存储机制
  • OCR、图像分类与目标检测
  • 浏览器指纹科普 | Do Not Track 是什么?
  • 客户端测试点
  • 上每网站建设/国外seo网站
  • 免费网站的资源可以发公众号吗/会计培训班
  • 公司做网站需要多少钱/北京百度搜索排名优化
  • 长沙网站建站公司/手机访问另一部手机访问文件
  • dw怎么做鲜花网站/天津seo招聘
  • 都有哪些js素材网站/培训师资格证怎么考