JVM工作原理与实战(七):类的生命周期-初始化阶段

专栏导航

JVM工作原理与实战

RabbitMQ入门指南

从零开始了解大数据


目录

专栏导航

前言

一、类的生命周期

1.加载(Loading)

2.连接(Linking)

3.初始化(Initialization)

4.使用(Using)

5.卸载(Unloading)

二、初始化阶段

1.初始化阶段分析

2.类的初始化触发方式

3.不执行初始化指令的特殊情况

4.继承关系下的初始化阶段

总结


前言

JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了类的生命周期、类的初始化阶段等内容。


一、类的生命周期

类的生命周期描述了一个类加载、连接、初始化、使用、卸载的整个过程。

1.加载(Loading)

加载阶段是类的生命周期的起始点。当应用程序首次需要使用某个类时,Java虚拟机(JVM)会负责加载这个类。加载是通过类的加载器(ClassLoader)完成的,它会查找并加载类的二进制数据。这个过程包括将类的字节码从文件系统、JAR文件或网络加载到内存中。

2.连接(Linking)

连接阶段是加载阶段的后续,它包括验证、准备和解析三个子阶段。

  • 验证(Verification):验证阶段主要是确保被加载的类文件数据符合JVM规范,没有安全方面的隐患,以及是否与应用程序的其它部分兼容。验证过程包括文件格式验证、元数据验证、字节码验证和符号引用验证。
  • 准备(Preparation):准备阶段是为类的静态变量分配内存,并设置默认的初始值。需要注意的是,准备阶段并不会执行任何初始化操作。
  • 解析(Resolution):解析阶段是将符号引用转换为直接引用。在Java中,符号引用是一个类的全限定名,而直接引用是一个直接指向内存中的地址的指针。解析阶段发生在运行时,而不是编译时。

3.初始化(Initialization)

初始化阶段是类加载过程中的最后一步,当准备和解析阶段完成后,JVM会执行类的构造器方法,这个方法是由编译器自动收集类中的所有类变量的赋值动作和静态代码块集合来的。需要注意的是,构造器方法中的代码只在类被首次使用时执行一次。

4.使用(Using)

一旦类被成功加载、连接并初始化后,就可以被实例化并用于执行应用程序的业务逻辑。在应用程序运行期间,类可能会被频繁地使用。

5.卸载(Unloading)

当应用程序不再需要某个类时,该类的实例以及与其相关的资源将会被回收,这个过程就是卸载。但是需要注意的是,只有当一个类不再被任何活动对象所引用时,它才会被卸载。另外,JVM的垃圾回收机制(Garbage Collection, GC)负责自动处理类的卸载和资源的回收。

二、初始化阶段

1.初始化阶段分析

初始化阶段是类生命周期中的关键阶段之一,主要涉及静态代码块的执行和静态变量的初始化。这个阶段是在类首次被加载到内存中或者在首次实例化这个类的对象时发生的。

在初始化阶段,会执行静态代码块中的代码,这些代码块在类定义中以static声明的部分。这些代码块仅在类首次被加载时执行一次,主要用于执行一些仅需在类加载时进行的初始化操作,如静态变量的赋值等。

此外,初始化阶段会执行字节码文件中clinit部分的字节码指令。clinit方法是Java编译器自动生成的特殊方法,用于执行所有类级别的初始化操作,包括对静态变量的赋值、静态代码块的执行等。clinit方法的执行顺序与Java源代码中编写的顺序一致,确保了按照源代码的顺序进行初始化。

案例:

public class Demo1 {
    public static int value = 1;

    static {
        value = 2;
    }

    public static void main(String[] args) {
    }
}

字节码信息:

<init>构造方法
mainMain方法
<clinit>初始化阶段执行

clinit部分的字节码指令:

0 iconst_1
1 putstatic #2 <init/Demo1.value : I>
4 iconst_2
5 putstatic #2 <init/Demo1.value : I>
8 return

指令解析:

iconst_1将常量1放入操作数栈
putstatic #2 <init/Demo1.value : I>从操作数栈中获取值设置到静态变量中

初始化步骤:

2.类的初始化触发方式

类的初始化可以通过以下几种方式触发:

  • 访问类的静态变量或静态方法。当程序首次访问类的静态变量或静态方法时,会触发类的初始化。需要注意的是,如果静态变量是final修饰的并且等号右边是常量,则不会触发初始化。

案例:

public class Demo1 {
    public static void main(String[] args) {
        int i = Test.i;
        System.out.println(i);
    }
}

class Test{
    static {
        System.out.println("初始化");
    }
    public static int i = 0;
}

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

-XX:+TraceClassLoading

运行结果:

  • 调用Class.forName(String className)方法。这个方法用于动态加载类,当调用该方法时,会触发类的初始化。

Class.forName(String className) 源码:

    @CallerSensitive
    public static Class<?> forName(String var0) throws ClassNotFoundException {
        Class var1 = Reflection.getCallerClass();
        return forName0(var0, true, ClassLoader.getClassLoader(var1), var1);
    }

    @CallerSensitive
    public static Class<?> forName(String var0, boolean var1, ClassLoader var2) throws ClassNotFoundException {
        Class var3 = null;
        SecurityManager var4 = System.getSecurityManager();
        if (var4 != null) {
            var3 = Reflection.getCallerClass();
            if (VM.isSystemDomainLoader(var2)) {
                ClassLoader var5 = ClassLoader.getClassLoader(var3);
                if (!VM.isSystemDomainLoader(var5)) {
                    var4.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
                }
            }
        }

        return forName0(var0, var1, var2, var3);
    }

    private static native Class<?> forName0(String var0, boolean var1, ClassLoader var2, Class<?> var3) throws ClassNotFoundException;

boolean var1参数表示是否初始化这个类:

案例: 

public class Demo1 {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> demo2 = Class.forName("Test.Demo2");
    }
}

class Demo2 {
    static {
        System.out.println("初始化");
    }
}

运行结果:

  • 创建一个类的对象。当使用new关键字创建一个类的对象时,会触发类的初始化。
  • 执行Main方法的当前类。当Java应用程序的入口点是Main方法时,会自动触发当前类的初始化。

案例: 

public class Demo3 {
    static {
        System.out.println("Demo3初始化");
    }
    public static void main(String[] args) throws ClassNotFoundException {
        new Demo4();
    }
}

class Demo4{
    static {
        System.out.println("Demo4初始化");
    }
}

运行结果:

3.不执行初始化指令的特殊情况

在字节码层面,初始化阶段是通过执行clinit方法来完成的。然而,值得注意的是,clinit方法并不是在所有情况下都会出现。以下是一些特定情况下不会执行clinit方法的情况:

  • 如果类中既没有静态代码块,也没有对静态变量进行赋值的语句,那么在加载类时不会执行clinit方法。这是因为clinit方法的目的是执行类级别的初始化操作,而在这种情况下,没有需要进行的初始化工作。

案例: 

public class Demo1 {
    public static void main(String[] args) {
        
    }
}

 字节码信息:

  • 如果类中有静态变量的声明,但没有对其进行赋值的语句,同样不会执行clinit方法。静态变量的初始化需要通过赋值语句来进行,如果只是声明但没有赋值,则不会被初始化。

案例: 

public class Demo1 {
    public static int i;
    public static void main(String[] args) {

    }
}

  字节码信息:

  • 如果静态变量的定义使用了final关键字,这类变量会在准备阶段直接进行初始化,因此在类加载时不会执行clinit方法。这是因为final修饰的静态变量在编译时就已经确定了值,不需要在运行时再进行初始化。

案例: 

public class Demo1 {
    public static final int i = 1;
    public static void main(String[] args) {

    }
}

  字节码信息:

4.继承关系下的初始化阶段

在继承关系下,类的生命周期中的初始化阶段变得更为复杂。子类和父类之间的初始化交互是理解这个过程的关键。

  • 直接访问父类的静态变量:在Java中,静态变量是类级别的变量,它们不属于任何一个对象实例,而是与类本身关联。当我们在子类中直接访问父类的静态变量时,只会触发父类的初始化,而不会触发子类的初始化。这是因为静态变量的初始化是类级别的操作,与子类的实例化过程无关。这种机制确保了父类静态变量的初始化在子类中使用之前已经完成。
  • 子类的初始化clinit调用之前,会先调用父类的clinit初始化方法:在Java字节码层面,类的初始化是通过执行clinit方法完成的。当一个子类被初始化时,它的clinit方法会被调用。但在子类的clinit方法执行之前,父类的clinit方法会被先调用。这种顺序确保了父类中的静态变量和静态代码块在子类使用之前已经完成初始化。这种机制确保了继承关系下的正确初始化顺序,使得子类可以依赖于父类中定义的静态变量和静态代码块。


总结

JVM是Java程序的运行环境,负责字节码解释、内存管理、安全保障、多线程支持、性能监控和跨平台运行。本文主要介绍了类的生命周期、类的初始化阶段等内容,希望对大家有所帮助。

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

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

相关文章

噬菌体序列分析工具PhaVa的使用和使用方法

github: 25280841/PhaVa: Adapting the phasefinder approach for identifying phase variation to long reads (github.com) 挺简单的&#xff0c;这里就不翻译了&#xff0c;大家看着直接用吧。 PhaVa PhaVa is an approach for finding potentially Phase Variable invert…

linux中的系统安全

一.账号安全 将非登录用户的shell设为/sbin/nologin 系统中用户有三种&#xff1a;超级管理员 普通用户 程序用户 前两种用户可以登录系统&#xff0c;程序用户不给登录 所以称为非登录用户 命令格式&#xff1a; usermod -s /sbin/nologin&#xff08;改已有用户&#…

【动态规划】【字符串】132.分割回文串 II

作者推荐 【动态规划】【字符串】扰乱字符串 本文涉及的基础知识点 动态规划 字符串 LeetCode132. 分割回文串 II 给你一个字符串 s&#xff0c;请你将 s 分割成一些子串&#xff0c;使每个子串都是回文。 返回符合要求的 最少分割次数 。 示例 1&#xff1a; 输入&#x…

11.2 Linux串口驱动框架

tty 驱动程序框架 tty 驱动程序从下往上分别是设备驱动层、行规程、终端虚拟化、TTY I/O层&#xff0c;它们的功能如下&#xff1a; 设备驱动层&#xff1a;用于驱动设备&#xff0c;如串口、显示器、键盘等。行规程&#xff1a;用于处理控制字符、回显输入数据、缓存输入数据…

矩阵的乘法

首先矩阵的乘法定义如下&#xff1a; #include <stdio.h> int main() { int i 0; int j 0; int arr[20][20] { 0 }; int str[20][20] { 0 }; int s[20][20] { 0 }; int n1 0; int n2 0; int m2 0; int z 0; int m1 0;…

使用IDEA官方docker插件构建镜像

此方法同样适用于jetbrains系列的其他开发软件 在IDEA中&#xff0c;如果是maven项目&#xff0c;可以使用插件 <plugin><groupId>com.spotify</groupId><artifactId>docker-maven-plugin</artifactId><version>1.2.2</version> &…

用于查询性能预测的计划结构深度神经网络模型--大数据计算基础大作业

用于查询性能预测的计划结构深度神经网络模型 论文阅读和复现 24.【X1.1】 在关系数据库查询优化领域&#xff0c;对查询时间的估计准确性直接决定了查询优化结果&#xff0c;进而影响到数据库整体的查询效率。但由于数据库自身的复杂性&#xff0c;查询时间受到数据分布、数据…

Linux操作实例 – 输入输出重定向

Linux操作实例 – 输入输出重定向 Input & Output Redirection Examples in Linux By Jackson 1. 前言 在操作计算机的时候&#xff0c;我们能够很容易通过键盘、鼠标给计算机输入信息&#xff08;例如&#xff1a;写公文、邮件&#xff0c;同时通过显示器得到输出。这就…

【AI视野·今日Sound 声学论文速览 第三十九期】Tue, 2 Jan 2024

AI视野今日CS.Sound 声学论文速览 Tue, 2 Jan 2024 Totally 7 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Sound Papers Enhancing Pre-trained ASR System Fine-tuning for Dysarthric Speech Recognition using Adversarial Data Augmentation Authors Huimen…

数据安全保护体系的设计原则

目录 引言 数据的分类分级原则 数据的分类分级是个长期且动态的过程 数据的分类分级应结合实际应用和业务特性 建立数据分类分级制度和体系也是非常重要的 最小化原则 企业需要对数据访问的用户进行身份验证 企业需要明确用户访问数据的目的是什么 企业需要梳理数据访问…

CentOS 7 安装 PPTP

环境&#xff1a; 阿里云试用机&#xff1a; 外网IP&#xff1a;114.55.80.150 内网IP&#xff1a;172.28.11.92 一、服务器安装 PPTP 1、安装 yum install epel-release -y 2、安装pptp yum install pptpd iptables-services -y 3、修改配置 vim /etc/pptpd.conf# 最…

DS|二叉树

题目一&#xff1a;DS二叉树 -- 二叉树构建与遍历 题目描述&#xff1a; 给定一颗二叉树的逻辑结构如下图&#xff0c;&#xff08;先序遍历的结果&#xff0c;空树用字符‘#’表示&#xff0c;例如AB#C##D##&#xff09;&#xff0c;建立该二叉树的二叉链式存储结构&#xf…

【面试高频算法解析】算法练习5 深度优先搜索

前言 本专栏旨在通过分类学习算法&#xff0c;使您能够牢固掌握不同算法的理论要点。通过策略性地练习精选的经典题目&#xff0c;帮助您深度理解每种算法&#xff0c;避免出现刷了很多算法题&#xff0c;还是一知半解的状态 专栏导航 二分查找回溯&#xff08;Backtracking&…

【代码随想录】刷题笔记Day46

前言 刚考完自辩&#xff0c;Chat回答举例什么的真方便。早上做组会PPT去了&#xff0c;火速来刷题&#xff01; 139. 单词拆分 - 力扣&#xff08;LeetCode&#xff09; 单词是物品&#xff0c;字符串s是背包&#xff0c;单词能否组成字符串s&#xff0c;就是问物品能不能把…

1.3进制,码(8421),化简规则、卡诺图化简、性质,触发器(转换与设计、应用),电路图,电路设计

十进制与原码、反码、补码之间的转换 正数的原码、反码、补码相同&#xff0c;符号位为0 负数的原码为、符号位1&#xff0c;二进制数 反码&#xff0c;符号位不变、其它取反&#xff0c; 补码为&#xff1a;反码最低有效位1 运算 卡诺图化简 奇偶校验码 检查1的个数&…

使用CentOS 7.6搭建HTTP隧道代理服务器

在现代网络环境中&#xff0c;HTTP隧道代理服务器因其灵活性和安全性而受到广泛关注。CentOS 7.6&#xff0c;作为一个稳定且功能强大的Linux发行版&#xff0c;为搭建此类服务器提供了坚实的基础。 首先&#xff0c;我们需要明确HTTP隧道代理的基本原理。HTTP隧道代理允许客户…

字节填充与0比特填充以及数据链路的基本问题

目录 字节填充&#xff1a; 比特填充&#xff1a; 数据链路有三个基本问题 1.封装成帧 2.透明传输 3.差错检测 首先介绍一下PPP的帧结构&#xff1a; 首部的第一个字段和尾部的第二个字段都是标志字段F(Flag)&#xff0c;规定为0x7E (符号“0x”表示它后面的字符是用十六…

python练习3【题解///考点列出///错题改正】

一、单选题 1.【单选题】 ——可迭代对象 下列哪个选项是可迭代对象&#xff08; D&#xff09;&#xff1f; A.(1,2,3,4,5) B.[2,3,4,5,6] C.{a:3,b:5} D.以上全部 知识点补充——【可迭代对象】 可迭代对象&#xff08;iterable&#xff09;是指可以通过迭代&#xff…

发票信息提取v1.2.0

程序介绍 “发票信息提取”是一款用于提取电子发票的PDF、XML文件中的开票信息到excel表格的软件&#xff0c;无需联网及进行复杂配置&#xff0c;打开即用。目前支持增值税电子发票&#xff08;非数电票&#xff09;原始PDF文件&#xff0c;及数电票的XML文件。 更新内容 增加…

【I2C】i2c-tools工具使用,以及开发调试

i2c调试 eeprom 手动创建eeprom设备调试&#xff0c;例如0x50 是FRU的地址&#xff0c;i2c-3是bus 创建设备 echo 24c32 0x50 > /sys/bus/i2c/devices/i2c-4/new_device如果设备正确&#xff0c;将成功被创建&#xff0c;并且生成/sys/bus/i2c/devices/4-0050/eeprom&am…
最新文章