瑞_JVM虚拟机_类的生命周期_初始化阶段 <clinit>

文章目录

    • 1 JVM虚拟机概述
    • 2 类的生命周期
      • 2.1 加载阶段
      • 2.2 连接阶段
      • 2.3 初始化阶段\<client> ★★★★★
        • 2.3.1 案例一
          • 解析字节码指令
        • 2.3.2 案例二
      • 2.3.3 小结
      • 2.3.4 代码中触发类的初始化的方式
        • 2.3.4.0 设置打印出加载并初始化的类
        • 2.3.4.1 方式一
        • 2.3.4.2 方式二
        • 2.3.4.3 方式三
        • 2.3.4.4 方式四
      • 2.4 使用阶段
      • 2.5 卸载阶段
    • 附:JDK1.8运行时数据区
    • 附:数据类型的初始值表

🙊前言:本文章为瑞_系列专栏之《JVM虚拟机》的类的生命周期篇的初始化阶段小节。由于博主是从B站黑马程序员的《JVM虚拟机》学习其相关知识,所以本系列专栏主要是针对该课程进行笔记总结和拓展,文中的部分原理及图解等也是来源于黑马提供的资料,特此注明。本文仅供大家交流、学习及研究使用,禁止用于商业用途,违者必究!

瑞&3l

1 JVM虚拟机概述

瑞:请参考《瑞_JVM虚拟机_概述》




2 类的生命周期

  类的生命周期描述了一个类加载、使用、卸载的整个过程

  类的生命周期一般分为五个阶段:加载 ➡️ 连接 ➡️ 初始化 ➡️ 使用 ➡️ 卸载

在这里插入图片描述

瑞:初始化阶段最重要,因为程序员可以干涉

  由于连接阶段操作很多,所以,又可以分为七个阶段:加载 ➡️ 验证 ➡️ 准备 ➡️ 解析 ➡️ 初始化 ➡️ 使用 ➡️ 卸载

在这里插入图片描述




2.1 加载阶段

瑞:请参考《瑞_JVM虚拟机_类的生命周期》

2.2 连接阶段

瑞:请参考《瑞_JVM虚拟机_类的生命周期》

2.3 初始化阶段<client> ★★★★★

  加载 ➡️ 连接 ➡️ 初始化 ➡️ 使用 ➡️ 卸载

瑞:初始化阶段就是执行static代码块赋值

在这里插入图片描述
  连接阶段中非 final 修饰的静态变量(static)保存的是初始值(上图中 int 数据类型的初始值为 0,点我查看数据类型的初始值表),而且是保存在堆区里面的class对象中。但是最终这个值应该是1(如上图),当这个变量存储的值为1的时候,我们才能拿去使用。而这个赋值为1的操作就是在初始化阶段完成的。

  初始化阶段会执行静态代码块中的代码,并为静态变量赋值

  初始化阶段会执行字节码文件中 clinit 部分的字节码指令

瑞:clinit可以拆解为: cl —— class 代表类,init代表初始化。即类的初始化

2.3.1 案例一
【案例】静态变量赋值

  在下面的案例代码中,声明了一个静态变量 value 赋值为 1 ,在静态代码块中将 value 值赋为 2 ,如下:

public class RayTest {public static int value = 1;static {value = 2;}public static void main(String[] args) {System.out.println("value = " + value);}
}

  执行后运行结果如下

	value = 2

  使用 jclasslib 工具查看字节码文件的方法信息,见下图

在这里插入图片描述

瑞:jclasslib 工具的安装可以参考《瑞_JVM虚拟机_概述》

  1️⃣ 其中 [0] <init>虽然我们没有写构造方法,但是编译器会帮我们自动生成默认无参构造方法

  2️⃣ 其中 [1] main 主方法

  3️⃣ 其中 [2] <clinit> 初始化阶段执行

  <clinit>方法中的字节码指令如下:

0 iconst_1
1 putstatic #7 <com/ray/onlytest/at2024/t03/t15/RayTest.value : I>
4 iconst_2
5 putstatic #7 <com/ray/onlytest/at2024/t03/t15/RayTest.value : I>
8 return

在这里插入图片描述

  《Java虚拟机规范》中putstatic指令是给类中的静态字段赋值,值从操作数栈中获取。iconst_常量值指令:将常量值放到操作数栈中(临时存放),生成常量。

  putstatic指令说明原文见下图⬇️

在这里插入图片描述

瑞:《Java虚拟机规范》官网地址:https://docs.oracle.com/javase/specs/index.html

解析字节码指令

  所以字节码指令执行步骤如下

0 iconst_1
1 putstatic #7 <com/ray/onlytest/at2024/t03/t15/RayTest.value : I>
4 iconst_2
5 putstatic #7 <com/ray/onlytest/at2024/t03/t15/RayTest.value : I>
8 return

  1️⃣ 编号0行:将常量1放入操作数栈中

在这里插入图片描述

  2️⃣ 编号1行:将操作数栈中的 1 赋值给常量池中编号为#7(你不一定是#7)的变量即RayTest.value,由于在连接阶段(准备阶段)中已经对静态变量RayTest.value分配内存并设置初始值 0 ,所以RayTest.value由 0 变为了 1

在这里插入图片描述

  3️⃣ 编号4行:将常量2放入操作数栈中

  4️⃣ 编号5行:将操作数栈中的 2 赋值给常量池中编号为#7(你不一定是#7)的变量即RayTest.value,所以RayTest.value由 1 变为了 2

2.3.2 案例二

  将案例一的两句静态代码对调顺序

public class RayTest {static {value = 2;}public static int value = 1;public static void main(String[] args) {System.out.println("value = " + value);}
}

  执行后运行结果如下

	value = 1

  <clinit>方法中的字节码指令如下:

0 iconst_2
1 putstatic #7 <com/ray/onlytest/at2024/t03/t15/RayTest.value : I>
4 iconst_1
5 putstatic #7 <com/ray/onlytest/at2024/t03/t15/RayTest.value : I>
8 return

在这里插入图片描述

  连接阶段 value 默认值设为了 0 ,然后在初始化阶段将 2 赋值给 value,再将 1 赋值给 value

2.3.3 小结

  clinit方法中字节码指令的执行顺序与Java中编写的顺序是一致的

2.3.4 代码中触发类的初始化的方式

2.3.4.0 设置打印出加载并初始化的类

添加-XX:+TraceClassLoading参数可以打印出加载并初始化的类

在这里插入图片描述

在这里插入图片描述

2.3.4.1 方式一

  1️⃣ 访问一个类的静态变量或者静态方法,注意如果这个变量是 final 修饰的并且等号右边是常量则不会触发类的初始化阶段(连接阶段就会直接给 final 修饰的常量赋值)。

public class RayTest {public static void main(String[] args) {int i  = RayTest2.i;System.out.println(i);}
}class RayTest2{static {System.out.println("init...");i = 486;}public static int i = 0;
}

  运行后发现,RayTest2初始化了(打印了init…)

在这里插入图片描述

  将上面代码中RayTest2.i修改为 final 修饰

public class RayTest {public static void main(String[] args) {int i  = RayTest2.i;System.out.println(i);}
}class RayTest2{static {System.out.println("init...");
//        i = 486;}public static final int i = 486;
}

  运行后发现,RayTest2并没有初始化(未打印init…)修改后RayTest2.i这个变量是 final 修饰的并且等号右边是常量486,则不会触发类的初始化阶段

在这里插入图片描述

2.3.4.2 方式二

  2️⃣ 调用Class.forName(String className)。

在这里插入图片描述

public class RayTest {public static void main(String[] args) throws ClassNotFoundException {Class<?> clazz = Class.forName("com.ray.onlytest.at2023.t12.t05.RayTest2");}
}class RayTest2{static {System.out.println("init...");}
}

在这里插入图片描述

2.3.4.3 方式三

  3️⃣ new一个该类的对象时。

2.3.4.4 方式四

  4️⃣ 执行Main方法的当前类。




2.4 使用阶段

瑞:请参考《瑞_JVM虚拟机_类的生命周期》




2.5 卸载阶段

瑞:请参考《瑞_JVM虚拟机_类的生命周期》




附:JDK1.8运行时数据区

在这里插入图片描述

附:数据类型的初始值表


每一种基本数据类型和引用数据类型都有其初始值(见下表)

数据类型初始值
int0
long0L
short0
char‘\u0000’
byte0
booleanfalse
double0.0
引用数据类型null



本文是博主的粗浅理解,可能存在一些错误或不完善之处,如有遗漏或错误欢迎各位补充,谢谢

  如果觉得这篇文章对您有所帮助的话,请动动小手点波关注💗,你的点赞👍收藏⭐️转发🔗评论📝都是对博主最好的支持~


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/542030.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Kotlin:runBlocking导致App应用出现ANR问题实例

runBlocking简介 runBlocking 是常规函数&#xff1b; runBlocking 方法会阻塞当前线程来等待&#xff1b; runBlocking 的主线程会一直 阻塞 直到 runBlocking 内部的协程执行完毕。 runBlocking导致App应用出现ANR问题实例的效果 点击页面上的 刷新按钮 调用 refreshByrunBlo…

kubernetes-服务-ipvs-调度算法

kubernetes-服务-ipvs-调度算法 kubernetes-61、kubernetes中的网络通信插件1.1、calico1.2、flannel 2、service服务2.1、服务暴露/发现2.2、体验操作2.3、这个体验的流程图和解释 3、ipvs3.1、理解3.2、负载均衡的软件&#xff1a;LVS3.2.1、LVS的nat模式3.2.2、DR模式3.2.3、…

19113133262(微信同号)【征稿进行时|见刊、检索快速稳定】2024年区块链、物联网与复合材料与国际学术会议 (ICBITC 2024)

【征稿进行时|见刊、检索快速稳定】2024年区块链、物联网与复合材料与国际学术会议 (ICBITC 2024) 大会主题: (主题包括但不限于, 更多主题请咨询会务组苏老师) 区块链&#xff1a; 区块链技术和系统 分布式一致性算法和协议 块链性能 信息储存系统 区块链可扩展性 区块…

一场“猜成绩”大赛:ArrayList vs. LinkedList

今天我们将带来一场精彩绝伦的较量——ArrayList对阵LinkedList。 ArrayList它就像是一张大桌子&#xff0c;可以容纳各种各样的物品。 ArrayList是一个动态数组&#xff0c;具有随机访问的能力&#xff0c;这意味着我们可以在O(1)的时间复杂度内访问任意位置的元素。 它还具…

学点Java打小工_Day4_Homework

1 统计数字 1 int[] scores{0,0,1,2,3,5,4,5,2,8,7,6,9,5,4,8,3,1,0,2,4,8,7,9,5,2,1,2,3,9}; 求出上面数组中0-9分别出现的次数 &#xff08;双重for循环&#xff09; Testpublic void solveProblem1() {int[] scores {0,0,1,2,3,5,4,5,2,8,7,6,9,5,4,8,3,1,0,2,4,8,7,9,5,2,…

JMeter 并发测试和持续性压测详解

并发测试和持续性压测都是评估系统性能的常用方法&#xff0c;它们可以帮助开发人员发现并解决系统中的性能问题。本文来详细介绍下。 概念 并发测试&#xff1a; 旨在评估系统在同时处理多个用户请求时的性能。在这种 测试 中&#xff0c;系统会暴露于一定数量的用户负载下&…

MYSQL 索引 结构 以及常见优化

Mysql索引 索引概述 索引是帮助Mysql高效获取数据的排好序的数据结构 比如我们做查询的时候需要查询col289的数据 首先我们的数据在磁盘上的表不一定是挨着的&#xff0c;第一条数据插入后&#xff0c;可能其它程序在磁盘上写入了数据&#xff0c;然后再插入第二条&#xf…

【保姆级】GPT的Oops问题快速解决方案

GPT的"Oops"问题通常指的是GPT在处理请求时突然遇到错误或无法提供预期输出的情况。要快速解决这个问题&#xff0c;可以尝试以下分步策略&#xff1a; 确认问题范围&#xff1a; 首先&#xff0c;确认问题是偶发的还是持续存在的。如果是偶发的&#xff0c;可能是临…

Html提高——HTML5 新增的语义化标签

引入&#xff1a; 以前布局&#xff0c;我们基本用 div 来做。div 对于搜索引擎来说&#xff0c;是没有语义的。 但是在html5里增加了语义化标签&#xff0c;如 <header>&#xff1a;头部标签 <nav>&#xff1a;导航标签 <article>&#xff1a;内容标签 &…

phpcms头像上传漏洞引发的故事

目录 关键代码 第一次防御 第一次绕过 第二次防御 第二次绕过 第三次防御 第三次绕过 如何构造一个出错的压缩包 第四次防御 第四次绕过 本篇文章是参考某位大佬与开发人员对于文件包含漏洞的较量记录下的故事&#xff0c;因为要学习文件包含漏洞&#xff0c;就将大佬…

Dense Distinct Query for End-to-End Object Detection

摘要 对象检测中的一对一标签分配成功地消除了作为后处理的非极大值抑制&#xff08; NMS &#xff09;的需要&#xff0c;并使流水线端到端。然而&#xff0c;这引发了一个新的困境&#xff0c;因为广泛使用的稀疏查询无法保证高召回率&#xff0c;而密集查询不可避免地带来更…

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:ListItemGroup)

该组件用来展示列表item分组&#xff0c;宽度默认充满List组件&#xff0c;必须配合List组件来使用。 说明&#xff1a; 该组件从API Version 9开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。该组件的父组件只能是List。 使用说明 当List…