【基础篇】三、类的生命周期

news2024/11/27 2:26:46

文章目录

  • 1、类的生命周期
  • 2、加载阶段
  • 3、查看内存中的对象:hsdb工具
  • 4、连接阶段
  • 5、初始化阶段
  • 6、类生命周期的初始化阶段的触发条件
  • 7、类生命周期的初始化阶段的跳过
  • 8、练习题1
  • 9、练习题2
  • 10、数组的创建不会导致数组中元素的类进行初始化
  • 11、final修饰的变量,等号右边不是常量,此时类生命周期中会有初始化阶段

1、类的生命周期

在这里插入图片描述

  • 连接阶段又可分为验证、准备、解析这三步

  • 使用阶段即根据类创建实例(对象):

Demo demo1 = new Demo();
Class<Demo> clazz = Demo.class;
Demo demo2 = clazz.newInstance();
  • 类的卸载阶段和垃圾回收挂钩

2、加载阶段

STEP1:类加载器根据全类名通过不同的渠道以二进制流的方式获取字节码信息。

这里的不同渠道,指除了本地磁盘上的字节码文件被JVM加载外,还有程序运行过程中动态代理生成的类(内存中),以及网络传输的类(早期Applet技术)

在这里插入图片描述

STEP2:JVM将字节码中的信息保存到方法区中,生成一个InstanceKlass对象(c++),保存了类的所有信息

这个方法区只是一个概念层的东西,不同的JVM,甚至不同版本的HotSpot,设计方法区时都用到了不同的内存空间,比如早期的HotSpot,用的是永久带,而新版本中用的是元空间
在这里插入图片描述

注意上面InstanceKlass对象中保存的字节码信息,还多了一些信息,用于实现特定功能,比如虚方法表是多
态的实现基础

STEP3:JVM在堆区生成一个和上面InstanceKlass对象类似的java.lang.Class对象

这个类的Class对象,用于Java开发时获取类的信息,以及存储静态字段的数据。注意静态字段之前是存方法区,但JDK8及以后,存堆区。

在这里插入图片描述

为什么方法区和堆区都存字节码信息?只留方法区的InstanceKlass对象可行吗?

不可行,原因有二:

  • InstanceKlass对象是使用c++来编写的一个对象,Java代码一般不能直接去操作用c++编写的对象,所以在堆上面创建了一个java.lang.class这种用Java语言包装之后的对象,这个是可以在Java代码中操作的

  • 堆区中Class对象包含的字段要少于方法区的InstanceKlass对象包含的字段,这也是出于安全考虑,很多字段是JVM底层使用的,开发者用不到,有了Class对象,JVM也就可以控制开发者访问数据的范围了

在这里插入图片描述
一句话,加载阶段是类加载器将类的信息加载到内存中,JVM在方法区和堆区各分配一个对象去保存类的信息。

3、查看内存中的对象:hsdb工具

JDK中带有hsdb工具,即HotSpot Debugger,位于SDK/lib/sa-jdi.jar,可查看JVM的内存信息

//JDK8下启动:
java -cp sa-jdi.jar sun.jvm.hotspot.HSDB
//和之前的java -jar不同,这个jar中的启动类有多个,需要指定某个启动类

JDK11中启动,在JDK的bin目录下cmd,然后执行:

jhsdb hsdb

写段测试代码并运行:

public class TestJvm{
	//静态变量,一会儿查看其在哪个区
	public static final int i = 0;
	public static void main(String[] args ) throws IOException{
		new TestJvm();
		//不输入,让程序别退出运行
		System.in.read();
	}
}

打开HSDB页面:

在这里插入图片描述

输入PID,PID可执行jps查看:

在这里插入图片描述

在这里插入图片描述

点击Tools,查看对象直方图:

在这里插入图片描述

搜索刚才的TestJvm对象,双击查看对象在内存中的地址、对象的名字、类的信息:

在这里插入图片描述

在这里插入图片描述

可以看到确实有Klass对象以及Class对象,且定义的静态变量i在Class对象中,即堆区。(靠,最后一步卡的跟狗一样,截个网课里的图算了)

在这里插入图片描述

4、连接阶段

连接阶段又分为以下三步:
在这里插入图片描述

STEP1:验证

验证字节码是否满足JVM规范,校验内容包括:

  • 文件格式验证(魔数)

在这里插入图片描述

  • class文件里的主版本号是否适配当前JVM:JDK8中HotSpot虚拟机对版本号的校验源码如下:简言之就是首先判断字节码的主版本号小于JDK支持的最小值45,再判断其是否小于等于当前运行环境JDK版本(+42)。而当主版本号刚好等于当前JDK版本时,再往后判断字节码的副版本号不能超过运行环境的副版本号

在这里插入图片描述

  • 基本信息验证,如必须有父类,super不能为空

在这里插入图片描述

  • 验证执行指令的语义,如不能 go to跳转到不存在的一行

在这里插入图片描述

  • 符号引用验证,例如是否访问了其他类中private的方法

STEP2:准备

该步骤是为静态变量(static)分配内存并设置初始值。

在这里插入图片描述

注意,此时给static变量赋的值是类型的默认值,不是源码里的值。(PS:JVM版本对应JDK8及以后)

在这里插入图片描述

但如果是final修饰的变量,那就是直接给源码中的值,因为其不可二次赋值,编译器认为,这个变量的值现在就可以确定了

在这里插入图片描述

STEP3:解析

该步骤是将常量池中的符号引用替换为直接引用。字节码中,是通过符号引用指向字节码常量池中的内容:

在这里插入图片描述

到这一步了,则直接使用内存中地址进行访问具体的数据。继续用前面的HSDB工具,输入PID后查看类浏览器:

在这里插入图片描述

可以看到此时superclass不再是之前的编号,而是内存地址。

在这里插入图片描述

5、初始化阶段

Java基础时,常说静态代码块在类加载时执行 ,更确切的说是类加载后的初始化阶段。初始化阶段做的事是执行静态代码块 + 为静态变量赋值。从字节码的角度来说,初始化阶段是执行clinit部分的字节码指令。

在这里插入图片描述

以上源码的字节码执行过程:

在这里插入图片描述

调整源码中静态代码块和静态变量的位置,发现字节码也跟着变:

在这里插入图片描述

结论:字节码的clinit方法中的执行顺序与Java中编写的顺序是一致的

6、类生命周期的初始化阶段的触发条件

  • 访问一个类的静态变量或者静态方法,注意变量是final修饰且等号右边是常量时不会触发初始化(因为连接阶段就会给这种变量赋源码中的值)
  • 调用Class.forName(String className)
  • new一个该类的对象时
  • 执行Main方法的当前类

代码演示:

在这里插入图片描述

也可添加JVM参数来查看加载并初始化的类:

-XX:+TraceClassLoading

同上的代码,再次运行:

在这里插入图片描述

同上的代码,改为final修饰且等号右边为常量:

在这里插入图片描述

再看Class.forName方法:

在这里插入图片描述

main方法执行,所在的类被加载并初始化。new对象,对应的类也被初始化:

在这里插入图片描述

7、类生命周期的初始化阶段的跳过

以下几种情况不进行初始化指令(clinit方法)执行:

  • 无静态代码块且无静态变量赋值语句
  • 有静态变量的声明,但是没有赋值语句
  • 静态变量的定义使用final关键字,并且等号右边是常量,这类变量会在准备阶段直接进行初始化

在这里插入图片描述

8、练习题1

分析输出结果:

在这里插入图片描述
在这里插入图片描述

分析:Test1类中有main方法,其执行,首先是Test1类被加载,初始化阶段静态代码块执行,输出D,往下执行输出A,再new Test1的对象,从字节码可以看到,实例代码块被放到了init方法字节码指令里,因此,最终结果:DACBCB

注意这里虽然new对象了,但不会再触发初始化了,因为前面的main方法执行已经触发并完成初始化了。一个类被加载并初始化,一般只执行一次。

9、练习题2

子类的初始化clinit调用之前,会先调用父类的clinit初始化方法。因此,输出:2
在这里插入图片描述
调整,去掉new B02这一行:访问子类从其父类继承的静态变量,只会初始化父类,因此输出1

在这里插入图片描述

再变下,让父类A02中的静态代码块提前到静态变量前,此时输出0,原因前面已提到:字节码的clinit方法中的执行顺序与Java中编写的顺序是一致的

在这里插入图片描述

10、数组的创建不会导致数组中元素的类进行初始化

在这里插入图片描述

靠,有点混乱了,梳理下。加载是加载,初始化是初始化,加载后面不一定有初始化这步,而静态代码块的执行是在初始化这一步。

11、final修饰的变量,等号右边不是常量,此时类生命周期中会有初始化阶段

final修饰的变量如果赋值的内容需要执行指令才能得出结果,会执行clinit方法进行初始化:

在这里插入图片描述

====

总结:

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java代码审计 - OutputStream.write() 期间持续发生 XSS 攻击

** java - OutputStream.write() 期间持续发生 XSS 攻击 ** 1 漏洞概述 我一直在研究有关持久 XSS 攻击的安全问题 将未经验证的数据发送到网络浏览器&#xff0c;这可能会导致浏览器执行恶意代码。 代码采用 Java 语言。 void output(OutputStream out){out.write(byteDa…

借贷协议 Tonka Finance:铭文资产流动性的新破局者

“Tonka Finance 是铭文赛道中首个借贷协议&#xff0c;它正在为铭文资产赋予捕获流动性的能力&#xff0c;并为其构建全新的金融场景。” 在 2023 年的 1 月&#xff0c;比特币 Ordinals 协议被推出后&#xff0c;包括 BRC20&#xff0c;Ordinals 等在内的系列铭文资产在包括比…

Zookeeper之快速入门

前言 本篇文章主要还是让人快速上手入门&#xff0c;想要深入的话可以通过书籍系统的学习。 简介 是什么 可用于协调、构建分布式应用。 本质上是一个分布式的小文件存储系统。提供基于类似于文件系统的目录树方式的数据存储&#xff0c;并且可以对树中的节点进行有效管理…

系列十七(面试)、请你谈谈RocketMQ的消息丢失问题

一、RocketMQ的消息丢失问题 1.1、概述 生产环境中为了保证服务的高可用&#xff0c;一般情况下都是采用集群的方式&#xff0c;RocketMQ也不例外&#xff0c;另外现在企业级的开发基本都是分布式微服务的模式&#xff0c;这就存在着跨网络传输数据的问题&#xff0c;而网络传…

一文详解Cookie以及Selenium自动获取Cookie

前言 以后数据获取途径以及数据资产绝对会是未来核心要素生产工具和资源之一&#xff0c;每个大模型都离不开更加精细化数据的二次喂养训练。不过现在来看收集大量数据的方法还是有很多途径的&#xff0c;有些垂直领域的专业数据是很难获取得到的&#xff0c;靠人力去搜寻相当…

C语言——小细节和小知识7

一、逆序字符串 1、递归1 #include <stdio.h> #include <string.h>void ReverseArray(char *str) {char temp *str;//1int len (int)strlen(str);*str *(str len - 1);//2*(str len - 1) \0;//3if(strlen(str 1) > 2)//只要字符串还大于2&#xff0c;就…

Rust之构建命令行程序(二):读取文件

开发环境 Windows 10Rust 1.74.1 VS Code 1.85.1 项目工程 这次创建了新的工程minigrep. 读取文件 现在&#xff0c;我们将添加读取file_path参数中指定的文件的功能。首先&#xff0c;我们需要一个样本文件来测试它:我们将使用一个包含少量文本的文件&#xff0c;多行包含一…

原来我们常用的现货白银方法都有缺点?

今天做现货白银交易的朋友容易有一个认知上的误区&#xff0c;就是他们在学习分析方法的时候&#xff0c;总是认为这个方法是无所不能的&#xff0c;应用在交易中&#xff0c;总能让自己盈利。如果不盈利&#xff0c;只是自己执行上出了错误&#xff0c;而不是策略有错&#xf…

Goal-Auxiliary Actor-Critic for 6D Robotic Grasping with Point Clouds

题目&#xff1a;基于点云的 6D 机器人抓取目标-辅助行为-评价 摘要&#xff1a;6D 机 器 人 抓 取超 越 自上 而 下捡 垃 圾桶 场 景是 一 项具 有 挑战 性 的任 务 。 以往基于 6 D 抓 取综 合和 机器 人运 动 规划 的解 决方 案 通常 在开 环设 置下 运 行&#xff0c; 对抓…

【React】echarts-for-react 的使用

文章目录 echarts-for-react &#xff1a;一个简单的 Apache echarts 的 React 封装配置项手册&#xff1a;https://echarts.apache.org/zh/option.html#title 安装依赖 $ npm install --save echarts-for-react# echarts 是 echarts-for-react的对等依赖,您可以使用自己的版本…

thingsboard前端缓存--nginx

thingsboardnginx thingsboard部署到阿里云服务器之后&#xff0c;由于登录界面要发送的文件很大&#xff0c;并且服务器的带宽目前有限&#xff0c;因此配置一个nginx&#xff0c;进行前端页面的一些缓存&#xff0c;参考了https://qianchenzhumeng.github.io/posts/Nginx%E5…

Spring高手之路-@Autowired和@Resource注解异同点

目录 相同点 不同点 1.来源不同。 2.包含的属性不同 3.匹配方式&#xff08;装配顺序&#xff09;不同。 ​编辑 4.支持的注入对象类型不同 5.应用地方不同 相同点 都可以实现依赖注入&#xff0c;通过注解将需要的Bean自动注入到目标类中。都可以用于注入任意类型的Bean…

SpringBoot 2 集成Spark 3

前提条件: 运行环境&#xff1a;Hadoop 3.* Spark 3.* ,如果还未安装相关环境&#xff0c;请参考&#xff1a; Spark 初始 CentOS 7 安装Hadoop 3 单机版 SpringBoot 2 集成Spark 3 pom.xml <?xml version"1.0" encoding"UTF-8"?> <pro…

c 语言, 随机数,一个不像随机数的随机数

c 语言&#xff0c; 随机数&#xff0c;一个不像随机数的随机数 使用两种方式获取随机数&#xff0c;总感觉使用比例的那个不太像随机数。 方法一&#xff1a; rand() 获取一个随机数&#xff0c;计算这个随机数跟最大可能值 RAND_MAX&#xff08;定义在 stdlib.h 中&#xf…

拓扑排序图解-Kahn算法和深度优先搜索

拓扑排序 是将一个有向无环图中的每个节点按照依赖关系进行排序。比如图 G G G存在边 < u , v > <u,v> <u,v> 代表 v v v的依赖 u u u, 那么在拓扑排序中&#xff0c;节点 u u u一定在 v v v的前面。 从另一个角度看&#xff0c;拓扑排序是一种图遍历&#…

掌握成功的关键:了解定位咨询如何让你的业务转型和增长

在当今的商业世界中&#xff0c;市场竞争变得前所未有的激烈。这不仅要求企业提供优质的产品和服务&#xff0c;还需要确保其在市场中的位置。在这种环境中&#xff0c;精确的市场定位不仅是一个优势&#xff0c;而是生存和发展的必需。 定位咨询的概念与重要性 定位咨询是一项…

影响多域名SSL证书价格的因素

多域名SSL证书可以同时保护多个域名站点&#xff0c;对于域名站点比较多的个人或者企事业单位&#xff0c;多域名SSL证书能够实现统一管理多个域名的SSL证书&#xff0c;节省了管理SSL证书的时间和成本。现在市面上的多数多域名SSL证书的价格不同&#xff0c;多域名SSL证书的价…

交叉验证的种类和原理(sklearn.model_selection import *)

交叉验证的种类和原理 所有的来自https://scikit-learn.org/stable/modules/cross_validation.html#cross-validation-iterators并掺杂了自己的理解。 文章目录 前言一、基础知识1.1 交叉验证图形表示1.2 交叉验证主要类别 二、部分交叉验证函数&#xff08;每类一个&#xff0…

ssm基于jsp技术的选课管理系统论文

基于JSP技术的选课管理系统设计与实现 摘要 如今&#xff0c;科学技术的力量越来越强大&#xff0c;通过结合较为成熟的计算机技术&#xff0c;促进了学校、医疗、商城等许多行业领域的发展。为了顺应时代的变化&#xff0c;各行业结合互联网、人工智能等技术&#xff0c;纷纷…