【从零开始学习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程序的运行机制。

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

smartkit巡检E9000设备

https://support.huawei.com/enterprise/zh/doc/EDOC1100325140/f6eeacd6 打开链接,里面的内容很详细。

海外服务器和国内服务器有什么样的区别呢

海外服务器和国内服务器有什么样的区别呢,其实呢在外形方面是大同小异,除了外形还有一些其他方面还存在这一些差异。 一,地理位置的差异。 海外服务器——有可能在中国数据中心之外的任何国家地区,例如美国服务器,韩…

2.环境变量及接口关联

一、环境变量以及全局变量 操作流程 1.点击environment 2.点击environment右侧号,新增环境变量 3.在变量中输入变量名以及变量值 4.回到collection页面,修改变量环境 5.在collection中通过{{变量名}}调用变量 变量定义 环境变量:环境变量…

k8s部署单机模式的minio和minio-client

k8s部署单机模式的minio和minio-client 一、k8s部署minio1.1说明1.2 yaml内容1.3 步骤1.3.1 创建资源1.3.2 查看启动日志1.3.3 查看svc并访问控制台 二、docker部署minio-client2.1 查找镜像2.2 运行镜像2.3 绑定minio server 一、k8s部署minio 1.1说明 项目使用minio&#x…

用python测试网络上可达的网络设备

用python测试网络上可达的网络设备 之前使用的os在python中执行ping测试网络中可达的目标,但是他在执行ping命令时脚本会将系统执行ping时的回显内容显示出来,有时这些回显并不是必要的。如果用脚本一次性ping成百上千台网络设备或者URL时会影响美观和阅…

12、组合模式(Composite Pattern,不常用)

组合模式(Composite Pattern),有时又叫作部分-整体模式,它是一种将对象组合成树状的层次结构的模式,用来表示“部分-整体”的关系,使用户对单个对象和组合对象具有一致的访问性。 它在我们树型结构的问题中…

5+铜死亡+预后模型+分型生信思路,热点搭配免疫相关思路

今天给同学们分享一篇生信文章“The pathogenesis of DLD-mediated cuproptosis induced spinal cord injury and its regulation on immune microenvironment”,这篇文章发表在Front Cell Neurosci期刊上,影响因子为5.3。 结果解读: 基因芯…

【OpenGauss源码学习 —— (RowToVec)算子】

VecToRow 算子 概述ExecInitRowToVec 函数ExecRowToVec 函数VectorizeOneTuple 函数 ExecEndRowToVec 函数总结 声明:本文的部分内容参考了他人的文章。在编写过程中,我们尊重他人的知识产权和学术成果,力求遵循合理使用原则,并在…

使用Python的PyQt实现财务综合计算

背景: 考核内容 使用 Python 编写程序代码,设计一个带交互界面的财务分析软件,并满足以下要求: PART1:《财务软件设计思路报告》 (30分) (1)编写《财务软件设计思路报告》,描述你编制这个财务软件的设计目标、应用场景、设计思路…

uniapp多行文本展开或收起(兼容h5、微信小程序,其它未测试)

文章目录 一、效果图展示1、收起2、展开3、文本过短时隐藏按钮【查看更多、收起】 二、代码实现原理:判断文本是否过短1、html2、css3、 js(1)data数据定义(2)获取文本高度(3) 获取行数&#xf…

【Vue】安装 vue-router 库报错 npm ERR! ERESOLVE unable to resolve dependency tree

问题描述 运行npm install vue-router,安装vue-router库,npm报错。 npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency tree npm ERR! npm ERR! While resolving: my-project0.1.0 npm ERR! Found: vue2.7.15 npm ERR! node_mod…

linux的权限741

741权限 在 Linux 中,文件和目录的权限由三组权限来定义,分别是所有者(Owner)、所属组(Group)和其他用户(Others)。每一组权限又分为读(Read)、写&#xff0…

Ubuntu18.04 本地安装CVAT标注工具

写在前面: 1、如果直接clone最新版本的cvat,python版本最好安装3.8的,因为其中部分代码的语法只有高版本的python才可以支持。 2、安装完成以后本地登陆可能出现"cannot connect to cvat server"的错误,可以从Cannot …

FluxMQ—2.0.8版本更新内容

FluxMQ—2.0.8版本更新内容 前言 FLuxMQ是一款基于java开发,支持无限设备连接的云原生分布式物联网接入平台。FluxMQ基于Netty开发,底层采用Reactor3反应堆模型,具备低延迟,高吞吐量,千万、亿级别设备连接&#xff1…

C语言 - 字符函数和字符串函数

系列文章目录 文章目录 系列文章目录前言1. 字符分类函数islower 是能够判断参数部分的 c 是否是⼩写字⺟的。 通过返回值来说明是否是⼩写字⺟,如果是⼩写字⺟就返回⾮0的整数,如果不是⼩写字⺟,则返回0。 2. 字符转换函数3. strlen的使⽤和…

css单行/多行 超出部分多行文本溢出隐藏

单行文本溢出 <div class"box"><div class"text">aaaaaaaaaaaaaaaaaaaaaaaaaaaa</div> </div>.box {width: 100px;height: 50px;background-color: pink;.text {/* 文本溢出 */white-space: nowrap;/* 不换行 溢出隐藏 */overflow…

leetcode:1422. 分割字符串的最大得分(python3解法)

难度&#xff1a;简单 给你一个由若干 0 和 1 组成的字符串 s &#xff0c;请你计算并返回将该字符串分割成两个 非空 子字符串&#xff08;即 左 子字符串和 右 子字符串&#xff09;所能获得的最大得分。 「分割字符串的得分」为 左 子字符串中 0 的数量加上 右 子字符串中 1…

MacPro外接显示器使用hidpi

前几年因为工作的原因&#xff0c;公司给配了MacPro&#xff0c;用了几年发现确实比Windows好用。唯一不爽的就是13寸的屏幕感觉有点小&#xff0c;所以最近就买了一个准4K的32寸带鱼屏显示器。接上之后发现总感觉怪怪的&#xff0c;字体有点发虚的感觉。搜了半天&#xff0c;发…

【EI征稿中#先投稿,先送审#】第三届网络安全、人工智能与数字经济国际学术会议(CSAIDE 2024)

第三届网络安全、人工智能与数字经济国际学术会议&#xff08;CSAIDE 2024&#xff09; 2024 3rd International Conference on Cyber Security, Artificial Intelligence and Digital Economy 第二届网络安全、人工智能与数字经济国际学术会议&#xff08;CSAIDE 2023&…

黑豹程序员-java发邮件,发送内容支持html,带多附件的案例

介绍 发邮件mail是常见的软件功能&#xff0c;下面利于spring和java的mail库实现发送内容支持html&#xff0c;带多附件的案例 开启SMTP邮件发送协议 谁提供的SMTP邮件服务&#xff0c;就找谁开启。QQ邮箱类似。 依赖 <!--Java MAil 发送邮件API--><dependency&g…