当前位置: 首页 > news >正文

JVM详解——类的加载

文章目录

  • 类的加载
    • 1、Java程序如何运行
    • 2、Java字节码文件
    • 3、类加载
    • 4、类加载的过程
    • 5、类加载器
    • 6、类的加载方式
    • 7、类的加载机制
    • 8、双亲委派机制
    • 9、破坏双亲委派机制

类的加载

在这里插入图片描述

1、Java程序如何运行

  • 首先通过Javac命令将.java文件编译生成.class字节码文件。
    Javac是Java编译命令,编译过程分为四步。

    1. 词法解析,通过空格分隔出单词、操作符、控制符等信息,形成信息流传递给语法解析器。
    2. 语法解析,将信息流按照Java语法规则组装成语法树。
    3. 语义分析,检查类型是否匹配、关键词是否使用合理、作用域是否正确等。
    4. 字节码生产,将经过1、2、3步骤生产的新型转换为字节码。
  • .class文件加载到JVM中经过一系列类加载流程,由解释器解释执行和JIT即时编译器将字节码文件编译成本地机器码执行。

    字节码必须通过类加载机制加载到JVM后方能执行,执行有三种模式,解释执行、JIT编译执行、JIT编译和解释器混合执行(主流JVM默认执行的方式)。混合模式优势在于解释器在启动时先解释执行,节省编译时间。
    解释执行: 来一行代码,解释一行,大部分不常用的代码,采用此种方式
    即时编译: 对于部分热点代码,虚拟机将该部分字节码编译生成机器指令,以提高Java虚拟机的运行效率

  • CPU调度线程执行本地机器码


2、Java字节码文件

Class文件本质上是一个以字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑的排列在class文件中。JVM根据其特定的规则解析该二进制数据,从而得到相关信息。Class文件采用一种伪结构来存储数据,它有两种类型:无符号数和表。

Class文件的结构属性:

  1. 魔数与class文件的版本:class文件头4个字节称为魔数,是class文件的标识
  2. 常量池:class文件的资源仓库,存储变量的属性、类型和名称;方法的属性、类型和名称等。
  3. 访问标志:表示该class的属性和访问类型,比如class是类还是接口,访问类型是public、private,类型是否被标记为final
  4. 类索引、父类索引、接口索引:一种描述的数据项目,class文件凭此确定类的继承和实现关系
  5. 字段表属性:描述类或接口中声明的变量。比如变量的作用域(public、private、protected)、是否是静态变量(static)、可变性(final)、数据类型(基本数据类型、对象、数组)等
  6. 方法表属性:描述方法的类型、作用域、返回值、参数、是否是重写或重载
  7. 属性表属性:描述某些场景专有的信息。比如字段表中的特殊属性、方法表中的特殊属性。

3、类加载

Class 文件中描述的各类信息都需要加载到虚拟机后才能使用。JVM 把描述类的数据从 Class 文件加载到内存,并对数据进行加载、验证、解析和初始化,最终形成可以被虚拟机直接使用的 数据类型,这个过程称为虚拟机的类加载过程

与编译时需要连接的语言不同,Java 中类型的加载、连接和初始化都是在运行期间完成的,这增加了性能开销,但却提供了极高的扩展性,Java 动态扩展的语言特性就是依赖运行期动态加载和连接实现的。


4、类加载的过程

一个类从被加载到虚拟机内存开始,到卸载出内存为止,整个生命周期经历加载验证、准备、解析、初始化、使用和卸载七个阶段,其中验证、解析和初始化三个部分称为连接。加载、验证、准备、初始化阶段开始的先后顺序是确定的,解析则不一定:可能在初始化之后再开始,这是为了支持 Java 语言的动态绑定。
在这里插入图片描述

  1. 加载:查找并加载类的二进制数据
    在加载阶段,虚拟机需要完成以下三个步骤:

    • 通过一个类的全限定名来获取其定义的二进制字节流。
    • 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
    • 在Java堆内存中生成一个代表这个类的java.lang.Class对象,作为对方法区中这些数据的访问入口。
  2. 验证: 确保被加载类的正确性
    确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。验证分为4个阶段:文件格式验证、元数据验证、字节码验证、符号引用验证。

验证阶段是非常重要的,但不是必须的,它对程序运行期没有影响,如果所引用的类经过反复验证,那么可以考虑采用-Xverifynone参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。

  1. 准备:为类的静态变量分配内存,并将其初始化为默认值
    准备阶段是正式为类变量分配内存并设置类变量初始值零值的阶段,这些内存都将在方法区中分配。
    注: 此时分配的是类变量(static),不包括实例变量。初始化的值是数据类型的默认零值,比如0、0L、null、false 等。

  2. 解析:把类中的符号引用转换为直接引用
    解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。符号引用就是一组符号来描述目标,可以是任何字面量。

直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄

  1. 初始化:JVM对类进行初始化赋值
    为类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要对类变量进行初始化。

在Java中对类变量进行初始值设定有两种方式:

  • 声明类变量是指定初始值
  • 使用静态代码块为类变量指定初始值

JVM初始化步骤:

  • 假如这个类还没有被加载和连接,则程序先加载并连接该类
  • 假如该类的直接父类还没有被初始化,则先初始化其直接父类
  • 假如类中有初始化语句,则系统依次执行这些初始化语句
  1. 使用:使用类的对象实例
    类访问方法区内的数据结构的接口, 对象是堆区的数据。
  2. 卸载:类被卸载出内存
    Java虚拟机结束,类被卸载出内存。
    Java虚拟机结束生命周期的情况:
    • 执行了System.exit()方法
    • 程序正常执行结束
    • 程序在执行过程中遇到了异常或错误而异常终止
    • 由于操作系统出现错误而导致Java虚拟机进程终止

5、类加载器

启动类加载器:
Bootstrap ClassLoader,负责加载存放在JDK的安装目录下的jre\lib中,或被-Xbootclasspath参数指定的路径中的,并且能被虚拟机识别的类库(如rt.jar,所有的java.*开头的类均被Bootstrap ClassLoader加载)。启动类加载器是无法被Java程序直接引用的。

扩展类加载器:

Extension ClassLoader,该加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载JDK安装目录下的jre\lib\ext目录中,或者由java.ext.dirs系统变量指定的路径中的所有类库(如javax.*开头的类),开发者可以直接使用扩展类加载器。

应用程序类加载器:

Application ClassLoader,该类加载器由sun.misc.Launcher$AppClassLoader来实现,它负责加载用户类路径(ClassPath)所指定的类,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。


6、类的加载方式

类加载有三种方式:

1、命令行启动应用时候由JVM初始化加载

2、通过Class.forName()方法动态加载

3、通过ClassLoader.loadClass()方法动态加载

Class.forName()和ClassLoader.loadClass()区别?

  • Class.forName(): ​将类的.class文件加载到JVM中,还会对类进行解释,执行类中的static块
  • ClassLoader.loadClass(): ​只会将.class文件加载到JVM中,不会执行static中的内容,只有在newInstance() 方法创建类对象时才会去执行static块
  • Class.forName(name, initialize, loader)带参函数也可控制是否加载static块。并且只有调用了newInstance()方法构造函数创建类的对象时才会加载static块

7、类的加载机制

  1. 缓存机制,缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区寻找该Class,只有缓存区不存在,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓存区。(修改了class后,需要重启虚拟机,程序的修改才会生效)
  2. 双亲委派机制, 如果一个类加载器收到了类加载的请求,首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上。因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,也就是无法完成该加载,子加载器才会尝试自己去加载该类。

8、双亲委派机制

  1. AppClassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。

  2. ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader去完成。

  3. 如果BootStrapClassLoader加载失败(例如在$JAVA_HOME/jre/lib里未查找到该class),会使用ExtClassLoader来尝试加载;

  4. ExtClassLoader也加载失败,则会使用AppClassLoader来加载,如果AppClassLoader也加载失败,则会报出异常ClassNotFoundException

双亲委派机制的好处:

  • 系统类防止内存中出现多份同样的字节码(比如自己写的String类JDK中的String类会优先使用JDK中的系统API)
  • 保证Java程序安全稳定运行
  • 解决了各个类加载器的基础类统一问题

9、破坏双亲委派机制

为什么要破坏双亲委派?

举个🌰: 我们常用数据库驱动Driver接口,Driver定义在JDK中,但其实现是各个数据库服务商,比如:MySQLMYSQL CONNECROR,因此DriverManger要加载各个Driver接口实现类进行管理,但是DriverManager是由启动类加载器进行加载的,而这个启动类加载器默认值加载JDK安装目录下面的lib文件下的类库,但我们真正要加载的是各个实现类,需要有应用程序类加载器进行加载,这个时候就需要启动类加载器委托应用程序类加载器去加载Driver实现类,从而破坏了双亲委派。

破坏方式:

  1. 自定义类加载,重写loadclass方法。双亲委派的机制都是通过这个方法实现的,这个方法可以指定类通过什么类加载器来进行加载,所以如果改写他的加载规则,相当于打破双亲委派机制。
  2. 线程上下文类加载器: 提供父类加载器访问子类加载器的行为。
    双亲委派很好的解决了各个类加载器的基础类统一问题,基础类总是被用户代码所调用,但是如果基础类又要重新调用用户代码,此时就与双亲委派模型的设计理念相违背。
    比如:JNDI服务(JDBC/JCE/JAXB/JBI)是Java的标准服务,它的代码是由启动类加载器进行加载的,但是JNDI的作用就是进行资源的集中管理和查找,它需要调用由(服务厂商提供的实现类)开发人员在classpath下的类代码,但是启动类加载器不会进行加载。
    所以引入线程上下类加载器,通过java.lang.Thread类的setContextClassLoader()方法进行设置。如果创建线程时还未设置,它会从父线程继承一个,如果在应用程序全局范围内没有设置,那么这个线程上下类加载器就是应用程序类加载器。
  3. Java热部署
    Java热部署的规范化模块是OSGi提供的,热部署实现的关键就是OSGi自定义了类加载器,它为每个模块都配了一个类加载器。当需要动态地更换一个模块的时候,就把模块连通这个模块的类加载器一起替换,从而实现了热替换。此时类加载器从树状结构变为了网状结构,有大量的层与层之间的类加载器,所以就打破了双亲委派模型。

参考文章:

  • https://pdai.tech/md/java/jvm/java-jvm-classload.html
  • https://blog.csdn.net/Wangxichuan_Jack/article/details/123711799
  • https://blog.csdn.net/Fqzzzzz/article/details/123989751
  • https://blog.csdn.net/weixin_45629285/article/details/128050932

相关文章:

JVM详解——类的加载

文章目录类的加载1、Java程序如何运行2、Java字节码文件3、类加载4、类加载的过程5、类加载器6、类的加载方式7、类的加载机制8、双亲委派机制9、破坏双亲委派机制类的加载 1、Java程序如何运行 首先通过Javac命令将.java文件编译生成.class字节码文件。 Javac是Java编译命令&a…...

Ubuntu最新版本(Ubuntu22.04LTS)安装nfs服务器及使用教程

目录 一、概述 二、在Ubuntu搭建nfs服务器  👉2.1 安装nfs服务器  👉2.2 创建nfs服务器共享目录  👉2.3 修改nfs服务器配置文件  👉2.4 重启nfs服务器 三、客户端访问nfs服务器共享目录  🎈3.1 在nfs客户端挂载服…...

Python-第九天 Python异常、模块与包

Python-第九天 Python异常、模块与包一、了解异常1. 什么是异常:2. bug是什么意思:二、异常的捕获方法1. 为什么要捕获异常?2. 捕获异常的语法3. 如何捕获所有异常?三、异常的传递性1.异常是具有传递性的四、Python模块1. 什么是模…...

博彩公司 BetMGM 发生数据泄露,“赌徒”面临网络风险

Bleeping Computer 网站披露,著名体育博彩公司 BetMGM 发生一起数据泄露事件,一名威胁攻击者成功窃取其大量用户个人信息。 据悉,BetMGM 数据泄漏事件中,攻击者盗取了包括用户姓名、联系信息(如邮政地址、电子邮件地址…...

初探Mysql反向读取文件

前言 Mysql反向读取文件感觉蛮有意思的,进行了解过后,简单总结如下,希望能对在学习Mysql反向读取文件的师傅有些许帮助。 前置知识 在Mysql中存在这样一条语句 LOAD DATA INFILE它的作用是读取某个文件中的内容并放置到要求的表中&#x…...

地图坐标系大全:常用地图坐标系详解与转换指南

介绍地图坐标系的基本概念和原理地图坐标系是用于描述地图上位置的数学模型。它可以用来表示地球表面上的任意一个点,使得这个点的位置可以在地图上精确定位。不同的地图坐标系采用不同的基准面和投影方式,因此会有不同的坐标系参数,不同的坐…...

使用 URLSearchParams 解析和管理URL query参数

介绍 首先 URLSearchParams是一个构造函数,会生成一个URLSearchParams对象,参数类型: 不传 | string | object | URLSearchParams, 并且遇到特殊字符它会自动帮我们encode 和 decode const ur…...

一台电脑安装26个操作系统(windows,macos,linux,chromeOS,Android,静待HarmonyOS)

首先看看安装了哪些操作系统1-4: windows系统 四个5.Ubuntu6.deepin7.UOS家庭版8.fydeOS9.macOS10.银河麒麟11.红旗OS12.openSUSE Leap13.openAnolis14.openEuler(未安装桌面UI)15.中标麒麟(NeoKylin)16.centos17.debian Edu18.fedora19.oraclelinux(特别…...

Python配置文件管理之ini和yaml文件读取

1. 引言 当我们设计软件时,我们通常会花费大量精力来编写高质量的代码。但这往往还不够,一个好的软件还应该考虑其整个系统,如测试、部署、网络等。其中最重要的一个方面是配置管理。 良好的配置管理应允许在任何环境中执行软件而不更改代码…...

实战一(下):如何利用基于充血模型的DDD开发一个虚拟钱包系统?

上一节课,我们做了一些理论知识的铺垫性讲解,讲到了两种开发模式,基于贫血模型的传统开发模式,以及基于充血模型的DDD开发模式。今天,我们正式进入实战环节,看如何分别用这两种开发模式,设计实现一个钱包系统。话不多说,让我们正式…...

webpack当中的代码分割详解

A.代码分割方法一:将原来的单入口文件改为多入口文件 将不同的文件例如js代码文件分为入口文件和测试文件,这个时候打包出来的代码就会根据不同的文件单独打包成属于他们自己的文件 例如以下为单入口文件: entry: ./src/js/index.js 多入口文件:(在输出…...

【SSM】Spring对IoC的实现方式DI详讲

控制反转的一种实现方式——依赖注入一、IoC 控制反转(Overview)依赖注入(DI)- Overview利用 IoC(控制反转)这种思想有什么好处呢?二、依赖注入的方式setter 方式(xml配置中的proper…...

【QT 5 相关实验-示波器-学习笔记-示波器组件练习与使用总结】

【QT 5 相关实验-示波器-学习笔记-示波器组件练习与使用总结】1、概述2、实验环境3、参考资料-致谢4、自我提升实验效果视频演示5、代码练习-学习后拆解-实验步骤(1)头文件部分-"mwaveview.h"(2)cpp文件部分-"mwav…...

二维数组中的查找(两种解法,各有千秋)

凡事都有可能,永远别说永远。——《放牛班的春天》今天一题为再一个行列都有序的二维数组中寻找一个目标值,我们第一时间想到的可能是很暴力的解法,例如从头到尾进行遍历,这样能做出来,但是借用武忠祥老师的一句话&…...

quartz使用及原理解析

quartz简介 ​ Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,完全由Java开发,可以用来执行定时任务,类似于java.util.Timer。但是相较于Timer, Quartz增加了很多功能: 持久性作业 - 就是保持调度…...

Datawhale组队学习:大数据 D2——分布式文件系统(HDFS)

妙趣横生大数据 Day2三、Hadoop 分布式文件系统(HDFS)1. 分布式文件系统2. HDFS 简介3. HDFS 体系结构4. HDFS存储原理数据冗余存储数据存储策略数据错误与恢复5. HDFS数据读写过程读写过程HDFS故障类型和其检测方法HDFS编程实验1. 本地和集群文件间操作2. 基本文件操作3. Hado…...

CCIE重认证-300-401-拖图题全

拖图 拖图题 编程 snippet;192.168.5.0,mask 255.255.255.0;number是192.168.5.0;mask是255.255.255.0 snippets;edit-config对config,loopback对name 100,address对primary,mask…...

如何动态的创建类?type的其他用法?什么是元类,如何自定义元类?

1、python中一切都是对象,类也不例外,type是object的子类,是创建类的类。 如何动态的创建一个类? 用脚丫子创建 用脑子创建 不会 不知道什么事动态类 大家可能会有一堆的疑惑,是的我也是有很多疑惑那让我们一起来探个…...

XCP实战系列介绍15-XCP故障排查指导

本文框架 1.概述2. 通过调试器排查2.1 打开Det功能2.2 如何确定Det ErrorCode3. 通过XCP应答报文排查3.1 FE报文组成及故障码对应关系3.2 举个例子1.概述 前面几篇文章我们介绍了基于Davinci开发工具的XCP配置指导,配好了,代码也生成了,但是程序一定能正常跑起来吗?就算软…...

吉林大学软件需求分析与规范(Software Requirements Analysis Specification)

chapter0课程简介:◼ 软件工程专业核心课程之一◼ 软件工程课程体系最前端课程◼ 主要内容:需求的基本概念,需求的分类,需求工程的基本过程,需求获取的方法、步骤、技巧,需求分析和建模技术,需求…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望

文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...

2025盘古石杯决赛【手机取证】

前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...

高防服务器能够抵御哪些网络攻击呢?

高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

Map相关知识

数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...

Python 包管理器 uv 介绍

Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...

【分享】推荐一些办公小工具

1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...

【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)

LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 题目描述解题思路Java代码 题目描述 题目链接:LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...

上位机开发过程中的设计模式体会(1):工厂方法模式、单例模式和生成器模式

简介 在我的 QT/C 开发工作中,合理运用设计模式极大地提高了代码的可维护性和可扩展性。本文将分享我在实际项目中应用的三种创造型模式:工厂方法模式、单例模式和生成器模式。 1. 工厂模式 (Factory Pattern) 应用场景 在我的 QT 项目中曾经有一个需…...