JVM类的声明周期

news2024/11/28 4:43:29

文章目录

  • 版权声明
  • 生命周期概述
  • 加载阶段
    • 查看内存中的对象
  • 连接阶段
    • 连接阶段之验证
    • 连接阶段之准备
    • 连接阶段之解析
  • 初始化阶段
    • 练习题目一
    • 练习题目二
    • 练习题目三
    • 练习题目四
  • 使用阶段
  • 卸载阶段
  • 总结

版权声明

  • 本博客的内容基于我个人学习黑马程序员课程的学习笔记整理而成。我特此声明,所有版权属于黑马程序员或相关权利人所有。本博客的目的仅为个人学习和交流之用,并非商业用途。
  • 我在整理学习笔记的过程中尽力确保准确性,但无法保证内容的完整性和时效性。本博客的内容可能会随着时间的推移而过时或需要更新。
  • 若您是黑马程序员或相关权利人,如有任何侵犯版权的地方,请您及时联系我,我将立即予以删除或进行必要的修改。
  • 对于其他读者,请在阅读本博客内容时保持遵守相关法律法规和道德准则,谨慎参考,并自行承担因此产生的风险和责任。本博客中的部分观点和意见仅代表我个人,不代表黑马程序员的立场。

在这里插入图片描述

生命周期概述

在这里插入图片描述
Java类的生命周期包括以下阶段:

  1. 加载(Loading):当Java程序需要使用某个类时,JVM会检查该类是否已经被加载,如果没有加载,JVM会从磁盘中读取该类的字节码文件并创建一个Class对象,然后将该Class对象存放在方法区中。

  2. 验证(Verification):在加载类的过程中,JVM会对该类的字节码进行验证,以确保它符合Java虚拟机规范,不会对虚拟机造成安全上的威胁。

  3. 准备(Preparation):在准备阶段,JVM会为类的静态变量分配内存并设置默认值(0或null),并将这些变量存放在方法区中。

  4. 解析(Resolution):在解析阶段,JVM会将类中的符号引用转换为直接引用,以便于JVM能够快速访问类中的方法和变量。

  5. 初始化(Initialization):在初始化阶段,JVM会执行类的初始化代码,包括静态变量赋值和静态代码块的执行。如果该类有父类,JVM会先初始化父类。

  6. 使用(Using):在使用阶段,JVM会调用类中的方法和访问类中的变量。

  7. 卸载(Unloading):当JVM确定某个类实例已经不再被使用时,会将该类的Class对象从方法区中移除,这个过程称为卸载。

加载阶段

  1. 加载(Loading)阶段第一步是类加载器根据类的全限定名通过不同的渠道以二进制流的方式获取字节码信息。程序员可以使用Java代码拓展的不同的渠道。
    在这里插入图片描述
  2. 类加载器在加载完类之后,Java虚拟机会将字节码中的信息保存到方法区中
    在这里插入图片描述
  3. 类加载器在加载完类之后,Java虚拟机会将字节码中的信息保存到内存的方法区中
    • 生成一个InstanceKlass对象,保存类的所有信息,里边还包含实现特定功能比如多态的信息
      在这里插入图片描述
  4. 同时,Java虚拟机还会在堆中生成一份与方法区中数据类似的java.lang.Class对象
    • 作用:在Java代码中去获取类的信息以及存储静态字段的数据(JDK8及之后)
      在这里插入图片描述
  • 对于开发者来说,只需要访问堆中的Class对象而不需要访问方法区中所有信息
    在这里插入图片描述

查看内存中的对象

  • 推荐使用 JDK自带的hsdb工具查看Java虚拟机内存信息。工具位于JDK安装目录下lib文件夹中的sa-jdi.jar中。
  • 启动命令:
    java -cp sa-jdi.jar sun.jvm.hotspot.HSDB
    

连接阶段

在这里插入图片描述

  1. 验证(Verification):在加载类的过程中,JVM会对该类的字节码进行验证,以确保它符合Java虚拟机规范,不会对虚拟机造成安全上的威胁。

  2. 准备(Preparation):在准备阶段,JVM会为类的静态变量分配内存并设置默认值(0或null),并将这些变量存放在方法区中。

  3. 解析(Resolution):在解析阶段,JVM会将类中的符号引用转换为直接引用,以便于JVM能够快速访问类中的方法和变量。

连接阶段之验证

  • 连接(Linking)阶段的第一个环节是验证,验证的主要目的是检测Java字节码文件是否遵守了《Java虚拟机规
    范》中的约束。这个阶段一般不需要程序员参与。

  • 主要包含如下四部分,具体详见《Java虚拟机规范》:

    1. 文件格式验证,比如文件是否以0xCAFEBABE开头,主次版本号是否满足当前Java虚拟机版本要求
      在这里插入图片描述
    2. 元信息验证,例如类必须有父类(super不能为空)
      在这里插入图片描述
    3. 验证程序执行指令的语义,比如方法内的指令执行中跳转到不正确的位置
      在这里插入图片描述
    4. 符号引用验证,例如是否访问了其他类中private的方法等。

  • 版本号的检测:Hotspot JDK8中虚拟机源码对版本号检测
    在这里插入图片描述
    • 主版本号不能高于运行环境主版本号,如果主版本号相等,副版本号也不能超过。

连接阶段之准备

  • 准备阶段为静态变量(static)分配内存并设置初始值
    在这里插入图片描述
  • 准备阶段只会给静态变量赋初始值,而每一种基本数据类型和引用数据类型都有其初始值。
    在这里插入图片描述
  • final修饰的基本数据类型的静态变量,准备阶段直接会将代码中的值进行赋值。
    在这里插入图片描述

连接阶段之解析

  • 解析阶段主要是将常量池中的符号引用替换为直接引用
  • 符号引用就是在字节码文件中使用编号来访问常量池中的内容。
    在这里插入图片描述
  • 直接引用不在使用编号,而是使用内存中地址进行访问具体的数据。
    在这里插入图片描述

初始化阶段

在这里插入图片描述

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

  • 初始化阶段会执行字节码文件中clinit部分的字节码指令
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • clinit方法中的执行顺序与Java中编写的顺序是一致的
    在这里插入图片描述

  • 以下几种方式会导致类的初始化

    1. 访问一个类的静态变量或者静态方法,注意变量是final修饰的并且等号右边是常量不会触发初始化。
    public class Demo1 {
        public static void main(String[] args) {
            int i = Demo2.i;
            System.out.println(i);
        }
    }
    
    
    class Demo2{
        static {
            System.out.println("初始化了...");
        }
        public static final int i = 0;
    }
    
    1. 调用Class.forName(String className)。
    public class Demo3 {
        public static void main(String[] args) throws ClassNotFoundException {
            Class<?> clazz = Class.forName("init.ways.Demo4");
        }
    }
    
    
    class Demo4{
        static {
            System.out.println("初始化了...");
        }
    }
    
    1. new一个该类的对象时。
    2. 执行Main方法的当前类。
    public class Demo5 {
        static {
            System.out.println("Demo5初始化了...");
        }
        public static void main(String[] args) throws ClassNotFoundException {
            new Demo6();
        }
    }
    
    
    class Demo6{
        static {
            System.out.println("Demo6初始化了...");
        }
    }
    
  • 添加-XX:+TraceClassLoading 参数可以打印出加载并初始化的类

练习题目一

  • 请给出运行结果,并说明原因
public class Test1 {
    public static void main(String[] args) {
        System.out.println("A");
        new Test1();
        new Test1();
    }
    public Test1(){
        System.out.println("B");
    }
    {
        System.out.println("C");
    }
    static {
        System.out.println("D");
    }
}
代码的执行结果为:

D
A
C
B
C
B

原因是:

1. 静态代码块 `static {...}` 会在类加载时执行,因此会先输出 "D"2. `main` 方法中先输出 "A"3. 接着创建了两个 `Test1` 对象,因此会分别调用两次构造方法 `public Test1() {...}`。
4. 在构造方法之前,非静态代码块 `{...}` 会先执行,因此会先输出 "C"5. 每次创建 `Test1` 对象时,都会执行一次非静态代码块和构造方法,因此会输出两次 "C" 和两次 "B"
  • 详细分析:
    在这里插入图片描述
  • 局部代码块(也可以叫做构造代码块) 每次创建对象,调用构造器之前,都会执行该代码块中的代码
    在这里插入图片描述

练习题目二

  • clinit指令在特定情况下不会出现,比如:如下几种情况是不会进行初始化指令执行的

    1. 无静态代码块且无静态变量赋值语句。
    2. 有静态变量的声明,但是没有赋值语句。
    3. 静态变量的定义使用final关键字,这类变量会在准备阶段直接进行初始化
      在这里插入图片描述
  • 直接访问父类的静态变量,不会触发子类的初始化

  • 子类的初始化clinit调用之前,会先调用父类的clinit初始化方法
    在这里插入图片描述

  • 如果把new B02()去掉,结果如下
    在这里插入图片描述

练习题目三

  • 数组的创建不会导致数组中元素的类进行初始化
    public class Test2 {
        public static void main(String[] args) {
            Test2_A[] arr = new Test2_A[10];
    
        }
    }
    
    class Test2_A {
        static {
            System.out.println("Test2 A的静态代码块运行");
        }
    }
    
    • 运行的结果是什么都没输出
  • 解释
    • 创建一个类的数组不会导致该类被初始化,没有创建 Test2_A 类型的实例对象,因此该类不会被加载。
  • 静态代码块是在类加载时进行初始化的,具体来说,当 JVM 加载一个类时,会先加载该类的父类(如果有父类的话),然后再加载该类本身。在加载类的过程中,JVM 会执行该类的静态代码块,以完成静态成员变量的初始化和其他一些静态操作。

练习题目四

  • final修饰的变量如果赋值的内容需要执行指令才能得出结果,会执行clinit方法进行初始化
public class Test4 {
    public static void main(String[] args) {
        System.out.println(Test4_A.a);
    }
}

class Test4_A {
    public static final int a = Integer.valueOf(1);

    static {
        System.out.println("Test3 A的静态代码块运行");
    }
}
  • 运行结果为:
Test3 A的静态代码块运行
1
public class Test4 {
    public static void main(String[] args) {
        System.out.println(Test4_A.a);
    }
}

class Test4_A {
    public static final int a =1;

    static {
        System.out.println("Test3 A的静态代码块运行");
    }
}
  • 运行结果为:
1
  • 如果一个类中定义了静态成员变量,并且这些成员变量都是编译期常量(比如使用 final 关键字修饰的常量),那么在访问这些常量时,编译器会直接将常量的值嵌入到字节码中,而不是在运行时动态计算。这个过程被称为编译期常量折叠(Compile-Time Constant Folding)。

  • 在这种情况下,如果静态代码块中的代码并没有涉及到这些常量,那么在访问这些常量时,并不会触发类的初始化,也就不会执行静态代码块中的代码。因此,如果一个类中定义了静态成员变量,并且这些成员变量都是编译期常量,并且静态代码块中的代码并没有涉及到这些常量,那么在访问这些常量时,不会触发静态代码块的执行。

使用阶段

  • 使用阶段是Java类生命周期中最重要的阶段,也是Java程序运行的核心阶段。在使用阶段中,Java类可以被创建、实例化、调用方法、访问变量等。
  1. 创建对象:在使用阶段中,可以通过关键字new创建一个类的对象

    String str = new String("Hello World!");
    
  2. 实例化:在使用阶段中,可以通过构造方法来实例化一个类的对象。

    Date date = new Date();
    
  3. 调用方法:在使用阶段中,可以通过对象来调用类中的方法

    int length = str.length();
    
  4. 访问变量:在使用阶段中,可以通过对象来访问类中的变量

    double pi = Math.PI;
    

卸载阶段

  • 卸载阶段是Java类生命周期中的最后一个阶段。在卸载阶段中,Java虚拟机会卸载不再需要的类和类加载器,从而释放内存空间。

  • 主要作用是清理内存,提高程序性能。

  • 一个类被卸载的条件是它的所有实例都已经被销毁,同时该类的类对象和类加载器也都已经被销毁

    • 当一个类被卸载时,Java虚拟机会先卸载该类的所有实例,然后卸载该类的类对象和类加载器。如果该类有子类,子类也会被卸载。如果父类和子类都没有被卸载,那么子类的类加载器也不会被卸载。
  • Java虚拟机在卸载类时,会调用该类的finalize()方法,该方法可以被子类重写以完成一些清理工作。在finalize()方法中,可以关闭文件、释放资源等操作。如果一个类没有重写finalize()方法,Java虚拟机会自动调用默认的finalize()方法。

  • 注意,Java虚拟机并不保证在任何时刻都会卸载一个类。在某些情况下,Java虚拟机可能会选择不卸载一个类,而是将其保留在内存中以提高程序性能。例如,如果一个类被频繁使用,Java虚拟机可能会将其保留在内存中以避免重复加载。

总结

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

  • 几个要点:
    1. 静态变量的定义使用final关键字,这类变量会在准备阶段直接进行初始化(除非要执行方法)。
    2. 直接访问父类的静态变量,不会触发子类的初始化。子类的初始化cinit调用之前,会先调用父类的cinit初始化方法。

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

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

相关文章

第四章 Python运算符与流程控制

系列文章目录 第一章 Python 基础知识 第二章 python 字符串处理 第三章 python 数据类型 第四章 python 运算符与流程控制 第五章 python 文件操作 第六章 python 函数 第七章 python 常用内建函数 第八章 python 类(面向对象编程) 第九章 python 异常处理 第十章 python 自定…

【Unity之UI编程】如何用UGUI搭建一个登录注册面板

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;UI_…

数据可视化:折线图

1.初看效果 &#xff08;1&#xff09;效果一 &#xff08;2&#xff09;数据来源 2.JSON数据格式 其实JSON数据在JAVA后期的学习过程中我已经是很了解了&#xff0c;基本上后端服务器和前端交互数据大多是采用JSON字符串的形式 &#xff08;1&#xff09;JSON的作用 &#…

【软件逆向】如何逆向Unity3D+il2cpp开发的安卓app【IDA Pro+il2CppDumper+DnSpy+AndroidKiller】

教程背景 课程作业要求使用反编译技术&#xff0c;在游戏中实现无碰撞。正常情况下碰撞后角色死亡&#xff0c;修改为直接穿过物体不死亡。 需要准备的软件 il2CppDumper。DnSpy。IDA Pro。AndroidKiller。 一、使用il2CppDumper导出程序集 将{my_game}.apk后缀修改为{my_…

AD9371 官方例程 NO-OS 主函数 headless 梳理

AD9371 系列快速入口 AD9371ZCU102 移植到 ZCU106 &#xff1a; AD9371 官方例程构建及单音信号收发 ad9371_tx_jesd -->util_ad9371_xcvr接口映射&#xff1a; AD9371 官方例程之 tx_jesd 与 xcvr接口映射 AD9371 官方例程 时钟间的关系与生成 &#xff1a; AD9371 官方…

SpringCloudAlibaba - 项目完整搭建(Nacos + OpenFeign + Getway + Sentinel)

目录 一、SpringCloudAlibaba 项目完整搭建 1.1、初始化项目 1.1.1、创建工程 1.1.2、配置父工程的 pom.xml 1.1.3、创建子模块 1.2、user 微服务 1.2.1、配置 pom.xml 1.2.2、创建 application.yml 配置文件 1.2.3、创建启动类 1.2.4、测试 1.3、product 微服务 1…

LIME低亮度图像增强

LIME低亮度图像增强 main.cpp #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <iostream> #include <opencv2/imgproc/imgproc.hpp> #include "lime.h"int main() {cv::Mat img_in cv::imread("…

Hello AIGC类杂志实验

shadow 类杂志是我们从6月开始孵化的一个小型内容产品。目前还在不断迭代升级中~~ 今天和大家聊聊心得体会&#xff1a; 从2023-06-12发布第一期以来&#xff0c;我和小杜一起打磨了前面几期&#xff0c;逐步找到稳定的内容表达方式后&#xff0c;由小杜独立负责此类杂志的更新…

c语言从入门到实战——函数递归

函数递归 前言1. 递归是什么&#xff1f;2. 递归的限制条件3. 递归举例3.1 举例1&#xff1a;求n的阶乘3.1.1 分析和代码实现3.1.2 画图推演 3.2 举例2&#xff1a;3.2.1 分析和代码实现3.2.2 画图推演 4. 递归与迭代 前言 函数递归是指一个函数直接或间接地调用自身&#xff…

13 Linux 蜂鸣器

一、蜂鸣器驱动原理 常用蜂鸣器分两种&#xff0c;有源蜂鸣器和无源蜂鸣器。 它们俩的区别&#xff1a;有源蜂鸣器具有内置的振荡器和驱动电路&#xff0c;无源蜂鸣器没有&#xff1b;源蜂鸣器只需简单的数字信号来控制&#xff0c;无源蜂鸣器需要外部电路或微控制器来提供特定…

Spring和SpringMVC总结

一、Spring IoC(Inversion of Control)中文名称&#xff1a;控制反转&#xff08;对象的创建交给Spring管理&#xff09;。DI(dependency injection )依赖注入。容器&#xff08;Container&#xff09;&#xff1a;放置所有被管理的对象。beans&#xff1a;容器中所有被管理的对…

Rust编程基础核心之所有权(下)

1.变量与数据交互方式之二: 克隆 在上一节中, 我们讨论了变量与数据交互的第一种方式: 移动, 本节将介绍第二种方式:克隆。 如果我们 确实 需要深度复制 String 中堆上的数据&#xff0c;而不仅仅是栈上的数据&#xff0c;可以使用一个叫做 clone 的通用函数。 看下面的代码…

国产数据库人大金仓Kingbase数据迁移工具

点击上方蓝字关注我 在做国产数据库适配过程中经常需要将现有数据库的数据迁移至国产数据库中&#xff0c;在适配在人大金仓Kingbase数据库时&#xff0c;可以使用KDTS进行数据迁移。 1. 支持迁移的数据库及对象 2. 迁移工具安装 地址&#xff1a;https://www.kingbase.com.c…

多输入多输出 | Matlab实现WOA-RBF鲸鱼算法优化径向基神经网络多输入多输出预测

多输入多输出 | Matlab实现WOA-RBF鲸鱼算法优化径向基神经网络多输入多输出预测 目录 多输入多输出 | Matlab实现WOA-RBF鲸鱼算法优化径向基神经网络多输入多输出预测预测效果基本介绍程序设计往期精彩参考资料 预测效果 基本介绍 Matlab实现WOA-RBF鲸鱼算法优化径向基神经网络…

农业水土环境与面源污染建模及对农业措施响应

目录 ​专题一 农业水土环境建模概述 专题二 ArcGIS入门 专题三 农业水土环境建模流程 专题四 DEM数据制备流程 专题五 土地利用数据制备流程 专题六 土壤数据制备流程 专题七 气象数据制备流程 专题八 农业措施数据制备流程 专题九 参数率定与结果验证 专题十 模型结…

【Python语言】比较四个数或多个数的大小

重点&#xff1a;sorted()函数可以对列表进行升序排序 思路&#xff1a;利用Python语言中的列表排序函数简单粗暴的解决比较大小问题 a int(input("请输入第一个数&#xff1a;")) b int(input("请输入第二个数&#xff1a;")) c int(input("请输…

反射型跨站点脚本攻击

测试过程: /ylpj/dwr/interface/%20idxIndexDicService/%22%3E%3Cscript%3Ealert(988)%3C/script%3E 解决方案: nginx添加 if ($args ~* "%3Cscript%3E") {return 400;}if ($request_uri ~* "%3Cscript%3E") {return 400; } 解决效果:

Java操作redis常见类型数据存储

目录 一、Java连接Redis 1.1 导入pom依赖 1.2 建立连接 二、Java使用Redis 2.1 字符串 String 2.2 哈希 Hash 2.3 列表 List 2.4 集合 Set 2.4 有序集合 Sorted Set 一、Java连接Redis redis与mysq都是数据库&#xff0c;java操作redis其实跟操作mysql的过程是差不多的…

C++性能优化笔记-6-C++元素的效率差异-7-类型转换

C元素的效率差异 类型转换signed与unsigned转换整数大小转换浮点精度转换整数到浮点转换浮点到整数转换指针类型转换重新解释对象的类型const_caststatic_castreinterpret_castdynamic_cast转换类对象 类型转换 在C语法中&#xff0c;有几种方式进行类型转换&#xff1a; // …

【每日一题】重复的DNA序列

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;哈希表方法二&#xff1a;哈希表滑动窗口位运算 写在最后 Tag 【哈希表】【位运算滑动窗口哈希表】【字符串】【2023-11-05】 题目来源 187. 重复的DNA序列 题目解读 找出字符串中重复出现的字符串。 解题思路 方法…