【深入浅出JVM原理及调优】「搭建理论知识框架」全方位带你深入探索类加载机制

全方位带你深入探索类加载机制

  • 专栏介绍
  • 前提准备
  • 面向人群
  • 知识脉络
  • 类加载是什么
    • 类加载和Class类对象的关系
      • JVM的预加载机制
        • 加载class文件的方式
    • 类加载过程(类的生命周期)
      • 加载阶段
        • 生成对应的Class文件
      • 连接操作
        • 验证(确保被加载的类的正确性)
          • 关闭验证阶段
        • 准备(类的静态变量分配内存+初始化默认值)
        • 案例分享
          • 指令赋值在初始化阶段
            • 引用类型和数组元素的初始值
          • 局部变量和类变量的赋值
          • 局部变量和类变量、常量的赋值
        • 解析(把类中的符号引用转换为直接引用)
          • 符号引用转换为直接引用
          • 解析后性能提升
          • 解析阶段的延迟
      • 初始化(类的静态变量分配内存+初始化默认值)
        • 声明时直接赋值
        • 静态代码块
        • JVM初始化步骤
        • JVM类初始化时机
      • 结束生命周期

专栏介绍

学习JVM需要一定的编程经验和计算机基础知识,适用于从事Java开发、系统架构设计、性能优化、研究学习等领域的专业人士和技术爱好者。

前提准备

  • 编程基础:具备良好的编程基础,理解面向对象编程(OOP)的基本概念,熟悉Java编程语言。
  • 数据结构与算法:对基本的数据结构和算法有一定了解,理解内存管理、线程操作等基本概念。

面向人群

学习本专栏以及本章内容的前提和适用人群如下:

  • Java开发人员:JVM是Java程序的核心执行引擎,因此Java开发人员需要深入了解JVM的工作原理和运行机制,以优化程序性能并解决相关问题。
  • 系统架构师和高级工程师:对系统整体性能、稳定性有较高要求的人群,有必要深入理解JVM以优化系统性能。
  • Java程序员和技术爱好者:具备一定Java编程经验,有意向深入了解JVM内部工作原理的人群。
  • 研究人员和学生:从事计算机科学相关研究或学习的人群,有兴趣深入研究JVM内部原理和优化方法。
  • JVM运维工程师:负责JVM性能优化、故障排查和调优的专业人员,需要对JVM有深入的理解。

知识脉络

每位Java开发者都了解到Java字节码是在Java运行时环境(JRE)上执行的。JRE包含了最为关键的组成部分:Java虚拟机(JVM),它负责分析和执行Java字节码。通常情况下,大多数Java开发者无需深入了解虚拟机的内部运行原理。即使对虚拟机的运行机制不甚了解,也不会对开发工作产生太多影响。然而,对JVM有一定了解的话,将更有助于深入理解Java语言,并解决一些看似困难的问题。

本专栏全面系统地剖析了特定虚拟机产品(即HotSpot,Oracle官方虚拟机)的实现,本人不仅深刻地讲解了看似深奥的原理,还提供了大量易于上手的实践案例,下面是总体的JVM相关的知识拓扑架构。

在这里插入图片描述

tips:当然还有一些最新的JVM特性未在这张图并非展示本专栏的全部内容,另外还包含了最新的JVM特性。

类加载是什么

类加载是Java中一个核心的概念,它主要指的是将类的 .class 文件中的二进制数值读取到内存中,对其进行处理,并储存在运行时数据区的方法区内。
在这里插入图片描述这个过程还会在堆区创建一个 java.lang.Class 类的对象,这个对象的主要作用是去封装那些已存储在方法区内的类相关数据结构,如下图案例所示。
在这里插入图片描述

类加载和Class类对象的关系

类加载的最终就是在堆区中形成这样一个 Class 对象,这个Class对象不仅对类在方法区内的数据结构进行了封装,而且,它还为开发者提供了访问接口,使得程序员能访问到方法区内的数据结构,这个标准化的接口是在开发中还是至关重要的,因为它为我们使用和理解Java类内部数据结构提供了极大的便利性和可操作性。

JVM的预加载机制

类加载器并不必须等到某个类被首次主动使用时再加载它。根据JVM的规范,类加载器可以预测某个类将要被使用时之前加载它。

如果在预先加载的过程中发现某个类的.class文件缺失或存在错误,类加载器会等到程序首次主动使用该类时报告LinkageError错误,但如果这个类一直没有被程序主动使用,类加载器则不会报告错误。

加载class文件的方式

在这里插入图片描述

  • 从本地系统中直接加载:可以直接从本地系统中加载所需文件,省去了通过网络获取的开销。 通过网络获取,典型场景:Web Applet:在Web
  • Applet场景下,可以通过网络获取所需的资源文件,如图片、样式表等。
  • 从压缩包中读取,成为日后jar、war格式的基础:从zip压缩包中读取文件可以作为以后生成jar、war格式文件的基础。这种方式可以减少文件大小,方便部署和分发。
  • 运行时计算生成,使用最多的是动态代理技术:运行时计算生成是使用最多的动态代理技术。通过动态代理,可以在运行时动态生成代理类,实现对目标对象的增强功能。
  • 由其他文件生成,典型场景:JSP应用从专有数据库中提取.class文件(较少见):在某些较少见的场景中,JSP应用可能需要从专有数据库中提取.class文件进行动态加载和运行。
  • 从加密文件中获取,典型的防Class文件被反编译的保护措施:使用从加密文件中获取功能可以有效防止Class文件被反编译的安全隐患,提供了对代码的保护措施。

类加载过程(类的生命周期)

类加载过程包括加载、验证、准备、解析和初始化这五个阶段,如下图所示。

在这里插入图片描述

加载阶段

加载阶段是整个类生命周期的第一个步,此阶段主要是为了查找并加载Java类的class字节码文件对应的二进制数据,JVM需要完成以下三件事情:

在这里插入图片描述

  1. 获取类字节码:通过类的全限定名获取对应的二进制字节流。
  2. 转化为运行时数据结构:将获取的字节流转化为方法区中的运行时数据结构。
  3. 创建Class对象:在Java堆中创建一个java.lang.Class对象,作为对方法区运行时数据的访问入口。
生成对应的Class文件

加载阶段具有最高的可控性,开发人员可以选择使用系统提供的类加载器来进行加载,也可以自定义自己的类加载器来实现加载操作。

一旦加载阶段完成,外部的二进制字节流按Java虚拟机所需的格式存储在方法区,同时在Java堆中创建了一个java.lang.Class对象。通过该对象,可以方便地访问方法区中的数据。

连接操作

JVM类加载系统中的连接阶段可以大致描述为以下三个步骤:验证、准备和解析。

在这里插入图片描述

验证(确保被加载的类的正确性)

验证是连接阶段的首要步骤,其主要目的是确保Class文件中的信息符合虚拟机的要求,并且不会对虚拟机的安全性造成威胁。

验证阶段通常包括以下四个检查动作:
在这里插入图片描述

  • 文件格式验证:验证Class文件的字节流是否符合规范格式,包括魔数和版本号的检查。
  • 元数据验证:验证类的元数据信息,例如父类与接口的继承关系、方法与字段的访问权限等。
  • 字节码验证:验证方法体中的字节码,包括操作数栈与局部变量表的类型匹配、跳转指令的目标合法性等。
  • 符号引用验证:验证符号引用是否能够正确解析,确保类、方法和字段的引用能够找到对应的定义。
关闭验证阶段

验证阶段虽然至关重要,但并非必需,因为它并不会直接影响程序运行。

在某些场合,例如:如果一个类已经被验证过多次,那么我们可以考虑使用 -Xverify:none 参数来关闭大部分验证过程。这样做的目的是为了缩减JVM在类加载过程中所需的时间,从而提升运行效率。

注意,关闭类验证可能会带来安全性风险,因此,在实际操作时要谨慎选择。

准备(类的静态变量分配内存+初始化默认值)

准备阶段是类加载过程中的一个阶段。在这个阶段,会为类变量分配内存并设置初始值,这些操作发生在方法区。以下是需要注意和优化的几个方面:

  1. 仅类变量(static)会在准备阶段进行内存分配,实例变量则会在对象实例化时,随着对象一起分配在Java堆中。

  2. 类变量的初始值通常是数据类型的默认值。而这些默认值是在Java代码中没有显式赋值的情况下自动赋予的。

例如,整数类型的默认值是0,长整型的默认值是0L,引用类型的默认值是null,布尔类型的默认值是false。

案例分享

假设有一个类变量的定义:

public static int value = 3;

注意,在准备阶段结束后,变量value的初始值实际上是0,而不是3。这是因为在准备阶段只进行了内存分配和默认初始值的设置,并没有执行具体的赋值操作。

指令赋值在初始化阶段

上面的赋值操作将在初始化阶段执行,通过生成类构造器(())方法来实现。在该方法中,会执行将value赋值为3的指令,这个初始化阶段会在实际执行Java方法之前进行。

  • 准备阶段后,变量value的初始值是0。
  • 初始化阶段后,变量value的值被赋值为3
引用类型和数组元素的初始值
  • 引用数据类型reference:如数组引用、对象引用等,如果没有对其进行显式地赋值而直接使用,系统都会为其赋予默认的零值,即null。
  • 数组Array:初始化没有对数组中的各元素赋值,那么其中的元素将根据对应的数据类型而被赋予默认的零值。
局部变量和类变量的赋值

对基本数据类型来说,对于类变量(static)和全局变量,如果不显式地对其赋值而直接使用,则系统会为其赋予默认的零值,而对于局部变量来说,在使用前必须显式地为其赋值,否则编译时不通过。

局部变量和类变量、常量的赋值
  • (final)被final修饰的常量则既可以在声明时显式地为其赋值,也可以在类初始化时显式地为其赋值,总之,在使用前必须为其显式地赋值,系统不会为其赋予默认零值。
  • (static final)对于同时被static和final修饰的常量,必须在声明的时候就为其显式地赋值,否则编译时不通过;
public static final int value = 3;

在准备阶段,变量value会被初始化为3,因为它同时被final和static修饰。

编译时Javac将会为value生成ConstantValue属性,在准备阶段虚拟机就会根据ConstantValue的设置将value赋值为3。可以理解为static final常量在编译期就将其结果放入了调用它的类的静态常量池中。

注意,不是一定被static final 定义的变量一定会被准备阶段就被放到静态常量池中。例如:static final a = getAConfig(),这种场景就不会放入常量池,因为只有在初始化阶段(Clinit)才能知道,真正执行出它的结果。

解析(把类中的符号引用转换为直接引用)

解析阶段,JVM会将静态常量池中的符号引用替换为直接引用,目的是为了提高代码的执行效率和减少运行时的解析开销。

解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。符号引用就是一组符号来描述目标,可以是任何字面量。

符号引用转换为直接引用
  • 符号引用:用于标识被引用内容的抽象引用形式,符号引用是在编译期间产生。

    • 通过描述符来提供对被引用项的唯一标识,目的是为了在编译期间进行类型检查和名称解析等操作,并生成对应的字节码指令,描述符:类名、方法名、字段名等,用于定位具体的符号。
  • 直接引用:直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄,可以直接访问到所引用的内容,直接引用是在解析阶段产生。

解析后性能提升

将符号引用替换为直接引用也可以减少运行时的解析开销,JVM可以在执行过程中直接访问到所引用的内容,而不需要再进行符号解析和查找过程,从而提高了代码的执行效率,提升程序的运行性能。

解析阶段的延迟

在这里插入图片描述
其中加载、验证、准备和初始化的顺序是确定的,而解析阶段的开始时间可以延迟到初始化阶段之后,解析阶段的延迟开始是为了支持Java语言的运行时绑定(也称为动态绑定或晚期绑定)。

注意,这些阶段是按顺序开始,并不是按顺序进行或完成,因为它们通常是相互交叉混合进行的,在一个阶段执行的过程中可能会调用或激活另一个阶段。

初始化(类的静态变量分配内存+初始化默认值)

初始化,为类的静态变量赋予真正的初始值,JVM负责对类进行初始化,主要对类变量进行初始化。在Java中对类变量进行初始值设定有两种方式:

声明时直接赋值

类的声明过程中直接为静态变量赋予初始值。例如:

public class MyClass {
    public static int myVariable = 10; // 直接赋值为10
}
静态代码块

静态代码块是在类加载过程中执行的代码块,在类被加载时自动执行。可以在静态代码块中为静态变量赋予初始值。例如:

public class MyClass {
    public static int myVariable;
    static {
        myVariable = 10; // 在静态代码块中赋值为10
    }
}
JVM初始化步骤
  • 假如这个类还没有被加载和连接,则程序先加载并连接该类
  • 假如该类的直接父类还没有被初始化,则先初始化其直接父类
  • 假如类中有初始化语句,则系统依次执行这些初始化语句

在这里插入图片描述

JVM类初始化时机

当对类的主动使用的时候才会导致类的初始化,类的主动使用包括以下六种:

在这里插入图片描述

  1. 创建类的实例,也就是使用new关键字来实例化一个类的对象。
  2. 访问某个类或接口的静态变量,或者对该静态变量进行赋值操作。
  3. 调用类的静态方法,可以通过类名直接调用静态方法。
  4. 反射,使用反射技术可以动态加载和使用类,通过Class.forName("com.xxx.ClassName")来获取类对象。
  5. 初始化某个类的子类时,其父类也会被自动初始化。
  6. Java虚拟机启动时被标明为启动类的类。

结束生命周期

在如下几种情况下,Java虚拟机将结束生命周期

  1. 执行System.exit()方法:通过调用System.exit()方法,可以显式地终止Java虚拟机的执行。
  2. 程序正常执行结束:当程序按照预期执行完所有的语句并正常结束时,没有遇到异常或错误。
  3. 程序在执行过程中遇到了异常或错误而异常终止:在程序执行过程中,如果遇到了无法处理的异常或发生了错误,程序会异常终止,这可能是由于代码逻辑问题、外部资源异常、或者其他意外情况导致的。
  4. 由于操作系统出现错误而导致Java虚拟机进程终止:在某些情况下,Java虚拟机进程可能会因为操作系统级别的错误(如内存不足、文件系统错误等)而被迫终止。

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

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

相关文章

Aop编程之动态代理

1、Java代理介绍 Java中的代理方式包括静态代理和动态代理。 静态代理在编译期间就确定了代理对象,动态代理是在运行期间动态生成代理对象。动态代理包括cglib动态代理和jdk动态代理,在目标对象有接口的情况下,可以使用jdk动态代理或者cglib…

使用numpy处理图片——基础操作

大纲 准备工作图片像素大小修改透明度 numpy是一款非常优秀的处理多维数组的Python基础包。在现实中,我们最经常接触的多维数组相关的场景就是图像处理。本系列将通过若干篇对图像处理相关的探讨,来介绍numpy的使用方法,以获得直观的体验。 本…

element-plus里el-date-picker日期选择器,默认值不显示的问题

官网文档给出的示例默认值也是没有没显示的。 找了很多方法&#xff0c;最终是给v-model"defaultTime"绑定初始值&#xff0c;如下代码&#xff0c;需要的可以改一下 <el-date-picker class"top_select" v-model"defaultTime" type"da…

数模学习day11-系统聚类法

本文参考辽宁石油化工大学于晶贤教授的演示文档聚类分析之系统聚类法及其SPSS实现。 目录 1.样品与样品间的距离 2.指标和指标间的“距离” 相关系数 夹角余弦 3.类与类间的距离 &#xff08;1&#xff09;类间距离 &#xff08;2&#xff09;类间距离定义方式 1.最短…

SpringIOC之support模块GenericGroovyApplicationContext

博主介绍&#xff1a;✌全网粉丝5W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…

职场日常英语口语,成人英语培训学校,柯桥学英语推荐哪里

“玩手机”用英语怎么说&#xff1f;你的第一反应是不是&#xff1a;play the phone&#xff1f; 在英语中&#xff0c;play这个动词通常表示“玩耍、娱乐、操纵”等意思&#xff0c;而手机是一种工具&#xff0c;不是玩耍的对象。 换句话说&#xff0c;我们“玩手机”&#xf…

Web缓存代理

目录 一.Web缓存代理 配置Nginx 缓存代理&#xff1a; 修改web服务器的配置文件&#xff1a; 修改192.168.233.10代理服务器的配置文件&#xff1a; 访问页面看看&#xff1a; 对于一些实时性要求非常高的页面或数据来说&#xff0c;就不应该去设置缓存&#xff0c;下面来…

Confluence跨大版本升级记录

初始版本7.4.18最终升级到了目前的最新版本8.7.1&#xff0c;在升级过程中遇到了很多问题&#xff0c;庆幸的是最后都解决了&#xff0c;记录一下。 背景 初始环境&#xff1a; OS&#xff1a;CentOS8.5 DB&#xff1a;10.8.8-MariaDB Confluence&#xff1a;7.4.18 公司…

c++学习笔记-STL案例-机房预约系统1-需求分析

1 机房预约系统需求 1.1 简单介绍 学校有几个规格不同的机房&#xff0c;由于使用经常出现“撞车”现象&#xff0c;现在开发一套预约系统&#xff0c;解决这一问题。 1.2 身份介绍 分别有三种身份使用该系统 学生代表&#xff1a;申请使用机房教师&#xff1a;审核学生的…

IP地址的网络安全防护和预防

网络安全对于保护个人和组织的信息资产至关重要&#xff0c;而IP地址是网络通信的基础。在这篇文章中&#xff0c;IP数据云将探讨IP地址的网络安全防护和预防措施&#xff0c;以确保网络的安全性和可靠性。 IP地址是互联网上每个设备在网络中的唯一标识符。有IPv4和IPv6两种类…

你Go代码写的像“鸭子”吗???

概 述 Go 语言也提供了接口类型&#xff0c;使得我们可以面向接口编程&#xff0c;将实现和接口分离。在我看来&#xff0c;软件的抽象之美也应该以此来表达&#xff0c;和 Java 语言不同的是 Go 并不是那么 “强制”&#xff0c;它使用了一种 鸭子类型 的方式让动态类型成为可…

75应急响应-数据库漏洞口令检索应急取证箱

必要知识点 第三方应用是选择性的安装的&#xff0c;比如mysql&#xff0c;如何做好信息收集&#xff0c;有没有爆过它的漏洞&#xff0c;和漏洞探针也是获取攻击者思路的重要操作&#xff0c;除去本身漏洞外&#xff0c;提前预知或口令相关攻击也要进行筛选。 排除三方应用…

古典吉他教师阿木:来自新疆的音乐才子

阿木,全名木合汤夏甫依克,于 1990 年 10 月 8 日出生在新疆这片美丽的土地上,是一位哈萨克族人。他是英皇认证古典吉他教师、中国社会艺术吉他考级考官、中国智慧工程研究会艺术教育委员会执行委员、新疆吉他艺术节发起人之一兼评审组组长。 阿木自幼受到哥哥的影响,对吉他产生…

85.乐理基础-记号篇-力度记号

内容来源于&#xff1a;三分钟音乐社 上一个内容&#xff1a;78.乐理基础-非常见拍号如何打拍子-CSDN博客 85-78之间的内容观看索引&#xff1a; 腾讯课堂-三分钟音乐社-打拍子&#xff08;20&#xff09;-总结、重点、练习与检验方法开始看 力度记号&#xff1a;p、f、mp、…

linux和windows机器下创建共享文件夹

1、安装samba sudo apt-get install samba 2、创建共享文件夹 sudo mkdir /smb sudo chmod 777 /smb/ 3、备份配置 sudo cp /etc/samba/smb.conf /etc/samba/smb.conf.bak 4、修改配置 sudo vim /etc/samba/smb.conf 添加以下信息到文件结尾 [SMB share] #描述信息…

Element+vue3.0 tabel合并单元格span-method

Elementvue3.0 tabel合并单元格 span-method :span-method"objectSpanMethod"详解&#xff1a; 在 objectSpanMethod 方法中&#xff0c;rowspan 和 colspan 的值通常用来定义单元格的行跨度和列跨度。 一般来说&#xff0c;rowspan 和 colspan 的值应该是大于等于…

锻炼身体固然好,爱护听力也重要

新年新气象&#xff01;你制定了那些新年计划和目标呢&#xff1f;相信健身一定在不少人的清单里。 根据美国调查网站 FiveThirtyEight 的报告的结果&#xff0c;新年目标的榜单前三是&#xff1a;减肥、健身、成为更好的人。 进入健身房&#xff0c;随着动感的音乐节奏朝着更强…

Map与JSONObject区别

相同点&#xff1a; 都可以存key-value&#xff1b;key是唯一的,如果key重复了会覆盖前面的 不同点&#xff1a; &#xff08;1&#xff09;JSONObject 不可以存空&#xff0c;Map可以存空。 &#xff08;2&#xff09;Map由jdk提供&#xff0c;JsonObject需要第三方jar包提供。…

pc下载apk文件到andriod开发板,并实现可视化

PC端安装APK下载器 点击下载 刷机精灵APK安装器 界面如下&#xff0c;可将下载好的apk文件&#xff0c;直接拖拽到该界面&#xff0c;然后点击安装全部按钮进行安装&#xff0c;安装过程中的具体状态会显示在具体的apk后面。 如下图&#xff0c;安装错误、安装完成等皆为apk安…

如何在IEC61850的ICD文件中添加新的DO节点

写在前面 恭喜“梅山剑客”粉丝突破1K&#xff0c;为了纪念这一伟大的时刻&#xff0c;今日发表此文&#xff0c; 纪念这神圣的时间节点&#xff0c;愿各位 青春永驻&#xff0c;笔耕不息。 本文参考链接&#xff1a; 1、61850开发知识总结与分享 2、IEC61850建模说明 1 简介…