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

前言:

在Java编程中,类的生命周期是指类从被加载到内存中开始,到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。

在本文中,我们将深入探讨类的生命周期,从类加载到内存中的各个阶段,以及在这个过程中发生的一些关键事件和操作。我们将了解类的加载、链接和初始化过程,以及类在内存中的存储结构和引用方式。

目录

前言:

 类的生命周期概述:

杂项知识点: 

 总结:


 类的生命周期概述:

在Java中,类的生命周期较为复杂,涉及到加载链接初始化使用卸载几个主要阶段。下面详细解释这些阶段:

  1. 加载(Loading): 这是类的生命周期的第一个阶段。在这个阶段,Java虚拟机(JVM)将类的.class文件从硬盘读入内存,为之创建一个 java.long.class 对象,并且在方法区中生成一个 InstanceKlass 对象来存储类的基本信息。而开发者只能操作java.long.class 对象而不能操作InstanceKlass 对象   需要注意的是:静态字段的数据存储在java.long.class中。这两个对象是关联的,可以相互找到。类的加载通常是由类加载器完成的,包括引导类加载器(Bootstrap ClassLoader)、扩展类加载器(Extension ClassLoader)和系统类加载器(System ClassLoader),还可以有用户自定义的类加载器。

  2. 链接(Linking): 加载完成后,类进入链接阶段,这个阶段又分为验证、准备和解析三个子阶段。

    • 验证(Verification):确保被加载的类符合JVM规范,没有安全问题。
    • 准备(Preparation):为类变量分配内存,并设置类变量的默认初始值 。并且需要注意的是:final修饰的基本数据类型的静态变量,准备阶段会直接将代码中的值进行赋值。
    • 解析(Resolution):将类、接口、字段和方法的符号引用转换为直接引用。
  3. 初始化(Initialization): 链接阶段之后,类会被初始化。在这个阶段,JVM 负责执行类的静态初始化器和静态初始化块。这包括执行静态字段的赋值语句和静态代码块。初始化是在类首次被使用时触发的,例如实例化、访问静态字段或调用静态方法时。

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

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

需要注意的是,Java中的类卸载并不是很常见,因为大多数应用的生命周期内,其加载的类都会一直被使用。只有在某些特定的场景下,比如热部署、动态加载和卸载插件的应用服务器等环境中,类的卸载才是必要的。

杂项知识点: 

1.类加载器的种类:

引导类加载器(Bootstrap ClassLoader):它是虚拟机的一部分,负责加载Java核心库(JAVA_HOME/jre/lib/rt.jar里面的类或-Xbootclasspath参数指定的路径中的类)。

扩展类加载器(Extension ClassLoader):它负责加载JAVA_HOME/jre/lib/ext目录中或java.ext.dirs系统属性指定路径中的类库。

系统类加载器(System ClassLoader):它根据Java应用的类路径(CLASSPATH)来加载Java应用类。

用户自定义加载器(User-Defined ClassLoaders):Java允许开发者通过继承java.lang.ClassLoader类的方式实现自己的类加载器。

2.什么是方法区:

        方法区(Method Area)是Java虚拟机(JVM)中的一块内存区域,存储了已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。方法区不同于堆(Heap)和栈(Stack),它是线程共享的,存储的是类相关的数据而不是对象实例。
需要注意的是,Java 8及之前的版本中,方法区是位于永久代(Permanent Generation)内的。而从Java 8开始,永久代被元空间(Metaspace)取代,方法区的数据被存储在元空间中。元空间是使用堆内存来实现的,但是它与Java堆是独立分配和回收的,因此方法区并不属于堆或栈。

3.哪几种情况会导致类的初始化:

  1. 创建类的实例:当首次创建某个类的实例时,该类将被初始化。
  2. 访问类的静态方法:首次调用类的任意一个静态方法时,该类将被初始化。
  3. 访问类或接口的静态字段:首次访问类或接口的静态字段(除了常量字段)时,该类或接口将被初始化。
  4. 反射:使用反射方式调用Class.forName()时,如果指定了要进行初始化,则会触发类的初始化。
  5. 初始化子类:如果一个类还未被初始化,则在其任何子类被初始化时,该父类也会被初始化。
  6. Java虚拟机启动时:定义了main方法的那个类将在程序开始执行时被初始化。
  7. 动态语言支持:如果一个java.lang.invoke.MethodHandle实例最后的解析结果为REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄对应的类没有进行过初始化,则需要先触发其初始化。
  8. 接口的默认方法:如果接口定义了默认方法,并且在首次调用其实现类的任何默认方法时,该接口会被初始化。
  9. 子类的初始化clinit调用之前,会先调用父类的clinit初始化方法。

4.哪几种方式不会导致类的初始化 

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

面试题实战:

说出以下代码运行结果:

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. 首先,在类加载过程中,会执行静态代码块中的代码。因此,静态代码块中的输出语句System.out.println("D");会被执行一次,打印出"D"。
  2. 接着,在main方法中,首先输出字符"A"
  3. 然后创建了第一个test5对象。在创建对象时,首先会执行实例代码块中的代码。因此,实例代码块中的输出语句System.out.println("C");会被执行一次,打印出"C"。接着,执行构造方法test5()中的代码,打印出"B"。
  4. 再创建第二个test5对象时,同样会执行实例代码块中的代码,打印出"C"。接着,执行构造方法test5()中的代码,打印出"B"。。

因此打印结果为DACBCB,你做对了吗? 

说出以下运行结果:

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方法,在main方法中创建了一个B02对象,由于B02继承自A02,因此会先初始化A02类。在A02类的静态代码块中,会输出"A02"并将a的值赋为1。接着初始化B02类,在B02类的静态代码块中,会输出"B02"并将a的值赋为2。最后,打印输出B02.a的值,即为2。

因此打印结果为:A02 B02 2,你做对了吗?

说出以下结果:

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  原因是因为:访问父类的静态变量,只初始化父类。 

总结:

        类的生命周期从加载、验证、准备、解析到初始化,每个阶段都有特定的任务和目标。在加载阶段,类的字节码数据被加载到内存中,并存储在方法区中。验证阶段验证类的字节码的正确性和安全性。准备阶段为静态变量分配内存并设置默认初始值,也将静态变量存储在方法区中。解析阶段将符号引用解析为直接引用,以便后续的方法调用。最后,在初始化阶段初始化类的静态变量和执行静态代码块的逻辑。

通过了初始化阶段的类可以进入正常的使用阶段,在这个阶段中,类的实例可以被创建,实例变量和方法可以被调用。但是,我们也要意识到类的生命周期并不只局限于这些阶段。类的卸载是一个不确定的阶段,只有当类的加载器认为它不再需要时,类才会被卸载。此外,类的生命周期还受到一些因素的影响,例如类的引用是否存在,是否被其他对象引用等。

了解类的生命周期对于理解Java程序的运行机制非常重要。它帮助我们理解类的加载、初始化和使用过程,并在必要时进行优化和资源管理。同时,深入了解类的生命周期也有助于我们编写更高效、可靠的Java应用程序。

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

如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力!

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

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

相关文章

Docker Container(容器)——6

目录: 什么是容器?容器生活案例?为什么需要容器?容器的生命周期 容器 OOM容器异常退出容器暂停容器命令清单容器命令详解 docker createdocker rundocker psdocker logsdocker attachdocker execdocker startdocker stopdocker r…

用Sketch for Mac轻松创作无限可能的矢量绘图

在如今的数码时代,矢量绘图软件成为了许多设计师和创意爱好者的必备工具。而在众多的矢量绘图软件中,Sketch for Mac无疑是最受欢迎的一款。它以其简洁易用的界面和强大的功能,让用户能够轻松创作出无限可能的矢量图形。 首先,Sk…

Jmeter入门

一、下载jmeter 官网下载 下载之后解压,在目录/bin下面找到jmeter.bat双击之后即可启动Jmeter。 二、使用 如下左图,选择语言为中文,可以修改测试计划的名称。如下右图,添加线程组 添加线程组 添加http请求 路径传参方式 …

vue3-自定义组件的使用及传值!!!

1.在vue项目中创建一个自定义组件(大多数页面中相同的样式,将其封装到组件中,可重复使用) 2.将公共组件引入到你想使用的页面中 结果显示如下: 3.为公共组件传值 4.公共组件接收值,显示在组件上 注意事项&a…

wappalyzer基于插件的网站开发技术解析工具

一、wappalyzer 解释:这是一款强大的工具,其主要能提供一种快速、可靠地检测网站所使用技术栈的方法,也就说说,服务器发来的信息都会被它剖析,然后分析出前端的技术栈,有时后端所使用的技术栈如果网页特征…

串口通信(1)-硬件知识

本文讲解串口通信的硬件知识。让读者快速了解硬件知识,为下一步编写代码做基础。 目录 一、概述 二、串口通信分类 2.1信息的传送方向进行分类 2.2同步通信和异步通信 三、串口协议 3.1 RS232 3.1.1 电气特性 3.1.2 连接器的机械特性 3.1.3 连接类型 3.1…

2023全国职业院校技能大赛信息安全管理与评估正式赛(模块三CTF)

全国职业院校技能大赛高等职业教育组信息安全管理与评估 \任务书\ 模块三 网络安全渗透、理论技能与职业素养 极安云科专注技能竞赛,包含网络建设与运维和信息安全管理与评估两大赛项,及各大CTF,基于两大赛项提供全面的系统性培训&#xf…

Pipenv环境配置+Pytest运行

环境配置 使用Pipenv进行虚拟环境管理,Pipfile为依赖模块管理文件。 安装pipenv:brew install pipenv根项目根目录下执行命令创建虚拟环境: pipenv install在Pycharm中指定项目运行的虚拟环境 :File->Settings->Project:-…

有趣的数学 数学建模入门三 数学建模入门示例两例 利用微积分求解

一、入门示例1 1、问题描述 某宾馆有150间客房,经过一段时间的经营,该宾馆经理得到一些数据:如果每间客房定价为200元,入住率为55%;定价为180元,入住率为65%;定价为160元…

第 5 部分 — LLM中红队的深入分析:数学和实证方法

一、说明 大型语言模型 (LLM) 领域正在迅速发展,需要强大的红队策略来确保其安全性和可靠性。 红队是一种模拟对抗性攻击来识别漏洞的方法,需要对理论基础和实际应用有深入的了解。在这个分析中,我深入研究了复杂的数学模型,并提供…

【k8s】使用Finalizers控制k8s资源删除

文章目录 词汇表基本删除操作Finalizers是什么?Owner References又是什么?强制删除命名空间参考 你有没有在使用k8s过程中遇到过这种情况: 通过kubectl delete指令删除一些资源时,一直处于Terminating状态。 这是为什么呢? 本文将…

自下而上-存储全栈(TiDB/RockDB/SPDK/fuse/ceph/NVMe/ext4)存储技术专家成长路线

数字化时代的到来带来了大规模数据的产生,各行各业都面临着数据爆炸的挑战。 随着云计算、物联网、人工智能等新兴技术的发展,对存储技术的需求也越来越多样化。不同应用场景对存储的容量、性能、可靠性和成本等方面都有不同的要求。具备存储技术知识和技…

关系型数据库-SQLite介绍

优点: 1>sqlite占用的内存和cpu资源较少 2>源代码开源,完全免费 3>检索速度上十几兆、几十兆的数据库sqlite很快,但是上G的时候最慢 4>管理简单,几乎无需管理。灵巧、快速和可靠性高 5>功能简…

JVM 性能调优

概述篇 面试题 讲讲你理解的性能评价及测试指标?(瓜子) 生产环境中的问题 生产环境发生了内存溢出该如何处理?生产环境应该给服务器分配多少内存合适?如何对垃圾回收器的性能进行调优?生产环境CPU负载飙高…

为什么近期白酒市场股票暴跌?2024年中高端酒企发展如何撬动市场?

为什么近期白酒市场股票暴跌?2024年中高端酒企发展如何撬动市场? 近期白酒市场股票暴跌的原因主要有两个方面:一是宏观经济环境的不景气,导致投资者对白酒行业的未来发展持谨慎态度;二是白酒市场竞争激烈,龙…

喜报|电巢科技获批教育部第三期供需对接就业育人项目100项!

项目获批 近日,教育部公布了第三期供需对接就业育人项目立项名单,电巢科技获批此次供需对接就业育人项目100项,其中包括定向人才培养培训项目40项、就业实习基地项目40项、人力资源提升项目20项。 在教育部高校学生司指导下,电巢…

Rust 学习

Rust 官网:https://www.rust-lang.org/zh-CN/ 1、Rust 简介 Rust 是一门注重安全(safety)、速度(speed)和并发(concurrency)的现代系统编程语言。Rust 通过内存安全来实现以上目标,但…

人体关键点检测2:Pytorch实现人体关键点检测(人体姿势估计)含训练代码

人体关键点检测2:Pytorch实现人体关键点检测(人体姿势估计)含训练代码 目录 人体关键点检测2:Pytorch实现人体关键点检测(人体姿势估计)含训练代码 1. 前言 2.人体关键点检测方法 (1)Top-Down(自上而下)方法 (2)Bottom-Up(自下而上)方法&#xff1…

听GPT 讲Rust源代码--src/tools(8)

File: rust/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs 在Rust源代码中,rust-analyzer是一个Rust编程语言的语言服务器。它提供了代码补全、代码重构和代码导航等功能来帮助开发者提高编码效率。 在rust-analyzer的代码目…

【改进YOLOv8】杂草识别系统:融合YOLO-MS的MS-Block改进YOLOv8

1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 研究背景与意义 随着人工智能技术的不断发展,计算机视觉领域的研究也取得了巨大的进展。目标检测是计算机视觉中的一个重要任务,它的应用范围广泛&#x…