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

JVM类加载机制—JVM类加载过程

一、概述

代码编译后,就会生成JVM(Java虚拟机)能够识别的二进制字节流文件(*.class)。而JVM把Class文件中的类描述数据从文件加载到内存,并对数据进行校验、转换解析、初始化,使这些数据最终成为可以被JVM直接使用的Java类型,这个说来简单但实际复杂的过程叫做JVM的类加载机制。

二、类加载过程

以自定义的Math类为例,分析类加载全过程。实例代码如下:

public class Math {public static final int initData = 666;public static User user = new User();public int compute() { //一个方法对应一块栈帧内存区域int a = 1;int b = 2;int c = (a + b) * 10;return c;}public static void main(String[] args) {Math math = new Math();math.compute();}}

以上面Math类为例,类加载全过程流程如下

1、JVM加载过程分析

其中在JVM中通过类加载器ClassLoader的loadClass方法对类进行装载。loadClass方法加载过程有以下几步:加载 -> 验证 -> 准备 -> 解析 -> 初始化 -> 使用 -> 卸载。

1.1 加载

所谓加载不是指的类加载机制,只是类加载机制中的第一步加载。这个阶段就是将Java类的字节码文件加载到机器内存中,并在内存中构建出Java类的原型——类模板对象

类模板对象,其实就是Java类在JVM内存中的一个快照,JVM将从字节码文件中解析出的常量池、类字段、类方法等信息存储到类模板中,这样JVM在运行期便能通过类模板调用Java类中的任意信息。创建的类模板对象的结构存储在方法区中。

一般来说加载分为以下几步:
a. 通过一个类的全限定名(包名与类名)获取此类的二进制字节流(Class文件)。
b.  将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。这里只是转化了数据结构,并未合并数据。(方法区就是用来存放已被加载的类信息,常量,静态变量,编译后的代码的运行时内存区域)
c.  在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。这个Class对象并没有规定是在Java堆内存中,它比较特殊,虽为对象,但存放在方法区中

1.2 验证

验证被加载后的类是否有正确的结构,类数据是否会符合虚拟机的要求,确保不会危害虚拟机安全。

验证作为链接的第一步,用于确保类或接口的二进制表示结构上是正确的,从而确保字节流包含的信息对虚拟机来说是安全的。

Java虚拟机规范中关于验证阶段的规则也是在不断增加的,但大体上会完成下面4个验证动作。

a. 文件格式验证:主要验证字节流是否符合Class文件格式规范,并且能被当前版本的虚拟机处理。
b.  元数据验证:主要对字节码描述的信息进行语义分析,以保证其提供的信息符合Java语言规范的要求。
c. 字节码验证:主要是通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。在第二阶段对元数据信息中的数据类型做完校验后,字节码验证将对类的方法体进行校验分析,保证被校验类的方法在运行时不会做出危害虚拟机安全的事件。
d. 符号引用验证:最后一个阶段的校验发生在虚拟机将符号引用转化为直接引用的时候,这个转化动作将在连接的第三阶段解析阶段发生。符号引用是对类自身以外(常量池中的各种符号引用)的信息进行匹配校验。

1.3 准备

给类或接口的静态变量分配内存空间,并且默认初始化这些字段。  并赋予默认值 如 int类型为0,String类型为null, boolean 类型为false等

1.4 解析

将常量池中的符号引用替换为直接引用过程。该阶段会把一些静态方法(符号引用 如main()方法)替换为执行数据所存内存的指针或句柄(直接引用),这就是所谓的静态链接过程(类加载期间完成)。动态链接实在程序运行期间完成的将符号引用替换为直接引用(如 实际使用的方法 math.compute())。

符号引用(Symbolic References):符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要可以唯一定位到目标即可。符号引用于内存布局无关,所以所引用的对象不一定需要已经加载到内存中。各种虚拟机实现的内存布局可以不同,但是接受的符号引用必须是一致的,因为符号引用的字面量形式已经明确定义在Class文件格式中。

直接引用(Direct References):直接引用时直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。直接引用和虚拟机实现的内存布局相关,同一个符号引用在不同虚拟机上翻译出来的直接引用一般不会相同。如果有了直接引用,那么它一定已经存在于内存中了。

1.5 初始化

初始化是类加载的最后一步。除了加载阶段,用户可以通过自定义的类加载器参与,其他阶段都完全由虚拟机主导和控制。到了初始化阶段才开始真正执行Java代码。

初始化主要工作就是对类的静态变量初始化为指定的值(如initData = 666)和执行静态代码块。

注意:主类在运行过程中如果使用到其它类,会逐步加载这些类。jar包或war包里的类不是一次性全部加载的,是使用到时才加载。

三、类加载时机

Java虚拟机规范中严格规定了有且只有五种情况必须对类进行初始化

a. 使用new创建类的实例或使用getstatic、putstatic读取或设置一个静态字段的值(放入常量池中的常量除外),以及使用invokestatic调用一个静态方法的时候,对应类必须进行过初始化。

b. 通过java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则要首先进行初始化。

c. 当初始化一个类的时候,如果发现其父类没有进行过初始化,则首先触发父类初始化。

d. 当虚拟机启动时,用户需要指定一个主类(包含main()方法的类),虚拟机会首先初始化这个类。

e. 使用jdk1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、RE_invokeStatic的方法句柄,并且这个方法句柄对应的类没有进行初始化,则需要先触发其初始化。

注意:虚拟机规范使用了“有且只有”这个词描述,这五种情况被称为“主动引用”,除了这五种情况,所有其他的类引用方式都不会触发类初始化,被称为“被动引用”。

1、主动引用情况的示例

new 对象触发类加载

/*** 分析JVM内存模型运行*/
public class Math {private final static int initData = 666;private static User user = new User();public int compute(){       //一个方法对应一个块栈帧内存区域int a = 1;int b = 2;int c = (a + b) * 10;return c;}public static void main(String[] args) {Math m = new Math();int result = m.compute();System.out.println(result);}static {System.out.println(initData);}
}

运行结果

反射触发类加载

示例代码如下

public class ReflectTest {static {System.out.println("ReflectTest static block");}public static void main(String[] args) throws ClassNotFoundException {Class<?> clazz = Class.forName("com.test.jvm.classloader.ReflectTest");}
}

代码运行结果

加载子类会先加载父类

实例代码如下

public class ExtendTest {public static void main(String[] args) {System.out.println(new B().str);}//父类static class A {static {System.out.println("A static block");}}//子类static class B extends A {public String str = "B str";static {System.out.println("B static block");}}
}

示例运行结果

主类触发类加载

示例代码如下

public class HeapTest {private final String str = "0987654321";private final int data = 123456789;public static void main(String[] args) throws InterruptedException {System.out.println("验证堆内存溢出情况");List<HeapTest> heapTestList = new ArrayList<HeapTest>();while (true){HeapTest heapTest = new HeapTest();heapTestList.add(heapTest);//Thread.sleep(10);}}
}

示例运行结果

2、被动引用情况的示例

子类引用父类静态字段

通过子类引用父类的静态字段,对于父类属于“主动引用”的第一种情况,对于子类,没有符合“主动引用”的情况,故子类不会进行初始化。代码如下

public class ExtendTest {public static void main(String[] args) {//System.out.println(new B().str);System.out.println(B.value);}//父类static class A {//静态变量public static int value = 666;static {System.out.println("A static block");}}//子类static class B extends A {public String str = "B str";static {System.out.println("B static block");}}
}

示例运行结果

数组引用类

通过数组来引用类,不会触发类的初始化,因为是数组new,而类没有被new,所以没有触发任何“主动引用”条款,属于“被动引用”。代码如下:

//数组测试类
public class ArrayTest {//静态变量valuepublic static int value = 666;//静态块,父类初始化时会调用static{System.out.println("父类初始化!");}
}//主测试类
public class Test {public static void main(String[] args) {ArrayTest[] tests = new ArrayTest[10];}
}

运行结果

引用静态变量

静态常量在编译阶段就会被存入调用类的常量池中,不会引用到定义常量的类,这是一个特例,需要特别记忆,不会触发类的初始化!

//静态变量测试类
public class ConstClass {static{System.out.println("常量类初始化!");}public static final String HELLOWORLD = "hello world!";
}//主测试类
public class Test {public static void main(String[] args) {//ArrayTest[] tests = new ArrayTest[10];System.out.println(ConstClass.HELLOWORLD);}
}

运行结果

相关文章:

JVM类加载机制—JVM类加载过程

一、概述 代码编译后&#xff0c;就会生成JVM&#xff08;Java虚拟机&#xff09;能够识别的二进制字节流文件&#xff08;*.class&#xff09;。而JVM把Class文件中的类描述数据从文件加载到内存&#xff0c;并对数据进行校验、转换解析、初始化&#xff0c;使这些数据最终成…...

可变参数模板与包装器

抱歉&#xff1a;铁汁们&#xff0c;最近在做兼职&#xff0c;积累社会经验&#xff0c;多有拖欠&#xff0c;请多多包涵&#xff08;抱拳&#xff09; 引子&#xff1a;接上回我们讲了C11的几种新增&#xff0c;今天就来接着讲C11中比较有用的二个东西可变参数模板与包装器。…...

工业控制常用“对象“数据类型汇总(数据结构篇)

合理巧妙的数据结构会大大简化项目的编程工作量,所以任何项目前期第一步应该是设计巧妙的数据结构、封装对象属性。这样会使我们的编程快捷和高效。这篇博客作为数据类型汇总,会不间断更新。 1、普通电机轴对象 2、普通电机轴对象(详细结构变量) TYPE "udtMotorAxis&q…...

优雅处理枚举前端丢失大Long精度问题

1. 枚举-json处理&#xff08;前端 <> 后端 <> 数据库&#xff09; 前端传递 枚举code 后端响应 枚举code 表里存储 枚举code 内存处理 枚举对象 Getter AllArgsConstructor JsonFormat(shape JsonFormat.Shape.OBJECT) public enum SexEnum {MALE(0, "男&…...

【c/c++】 学习ector 容器笔记

c/c 学习ector 容器笔记 int 型的 vector 容器应该使用什么类型的索引&#xff1f; 对于 int 型的 vector 容器&#xff0c;应该使用 size_t 类型的索引。size_t 是一个无符号整数类型&#xff0c;它在标准库中广泛用于表示大小和索引。它足够大&#xff0c;可以表示任何标准…...

DN专业3D图形制作软件win/mac软件安装下载(附下载链接)

目录 一、软件概述 1.1 Adobe DN简介 1.2 Windows/Mac系统要求 Windows系统&#xff1a; Mac系统&#xff1a; 二、安装步骤 2.1 下载与解压 2.2 安装程序 2.3 启动软件 三、使用教程 3.1 界面介绍 3.2 创建和编辑3D内容 3.3 合成与渲染 四、高级技巧与注意事项 …...

VSCode搭建Hzero(SpringCloud架构)后端开发调试环境

正常情况下我们使用IDEA开发Hzero&#xff0c;但是有的公司是不允许破解或者使用IDEA的&#xff0c;此时可以使用eclipse来替代也是可以的&#xff0c;最近尝试使用VSCode来开发调试发现了一些问题其中最大的问题是Vscdoe在绝大多数情况下是不能直接运行Hzero&#xff0c;使用插…...

【C++】OJ习题(初阶)

&#x1f680;个人主页&#xff1a;奋斗的小羊 &#x1f680;所属专栏&#xff1a;C 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 &#x1f4a5;1、字符串&#x1f4a5;1.1 字符串相加&#x1f4a5;1.2 验证回文字符串&#x1f4a5;1.3 反转…...

6.4K+ Star!一个强大的本地知识库问答系统,支持多格式文件和跨语言检索,为企业提供高效、安全的数据洞察……

https://github.com/netease-youdao/QAnything 【阅读原文】跳转Github项目 转自AIGC创想者 项目简介 QAnything 是一个基于本地知识库的问答系统&#xff0c;它能够理解和回答基于任何类型文件的问题。 QAnything支持的文件格式非常广泛&#xff0c;包括PDF、Word、PPT、XL…...

mvn编译的时候出现Perhaps you are running on a JRE rather than a JDK 解决方法

目录 1. 问题所示2. 原理分析3. 解决方法1. 问题所示 mvn编译的时候出现如下问题: [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.13.0:compile (default-compile) on project yudao...

React原理之Fiber详解

前置文章&#xff1a; React原理之 React 整体架构解读React原理之整体渲染流程 -----读懂这一篇需要对 React 整体架构和渲染流程有大致的概念 &#x1f60a;----- 在React原理之 React 整体架构解读中&#xff0c;简单介绍了 Fiber 架构&#xff0c;也了解了 Fiber 节点的…...

远离“优越感”陷阱,拥抱美好人生

在人生的漫长旅程中,我们不断地与他人相遇、相知、相交,在各种关系中寻找温暖、支持与成长。然而,并非所有的关系都如我们所愿,有些关系甚至可能成为我们前进道路上的阻碍。正如我们所知,唯利是图者不可交,但有一种关系比索要金钱更值得警惕,那就是找你索取满足感的关系…...

Redis的线程模型

Redis作为一种基于内存的高性能键值对数据库&#xff0c;其线程模型和IO模型是实现高性能的关键因素。以下将详细探讨Redis的线程与IO模型&#xff0c;内容不少于2000字。 一、Redis的线程模型 Redis的线程模型是理解其高性能的重要基础。在Redis的发展过程中&#xff0c;其线…...

ubuntu24.04安装nginx1.24

ubuntu安装nginx 更新包索引 sudo apt update安装nginx sudo apt install nginx确认安装成功并检查Nginx版本 nginx -v启动Nginx服务 sudo systemctl start nginx设置Nginx开机自启 sudo systemctl enable nginx在浏览器中访问 http://<your_server_IP> 来确认Nginx…...

一款好看的WordPress REST API 主题

介绍&#xff1a; 主题特色&#xff1a; 使用Nuxtjs WordPress Rest Api 实现前后端分离&#xff0c;可完成多端部署&#xff1b; 主题支持自动切换黑夜模式。 使用说明&#xff1a; service 目录为wordpress主题文件&#xff0c;需要拷贝到wordpress主题目录下&#xff0…...

《5G 与区块链融合:智能城市服务质量的飞跃》

在科技飞速发展的时代&#xff0c;5G 技术的普及正以前所未有的速度改变着我们的生活&#xff0c;而区块链技术的兴起也为各领域带来了创新的解决方案。当这两种前沿技术相互结合&#xff0c;将为智能城市的发展注入强大动力&#xff0c;显著提升服务质量&#xff0c;开创更加便…...

前后端分离开发:用 Apifox 高效管理 API

目录 1.前后台分离开发介绍 2.API 2.1 APIfox介绍 2.2 接口文档管理 1.前后台分离开发介绍 前端开发有2种方式&#xff1a;「前后台混合开发」和「前后台分离开发」。 前后台混合开发&#xff0c;顾名思义就是前台后台代码混在一起开发&#xff0c;如下图所示&#xff1a…...

Go Channel 详解

概述 在 Go 语言中&#xff0c;channel 是一种用于在 goroutine 之间传递数据的机制。它提供了同步和通信的能力&#xff0c;使得并发编程变得更加简单和安全。Channel 在 Go 语言中的设计是类型安全的&#xff0c;并且支持发送和接收两种操作。 基本概念 创建通道 创建一个…...

使用FModel提取游戏资产

使用FModel提取游戏模型 前言FModel简介FModel安装FModel使用初次使用资产预览资产导出 附录dumperDumper-7生成usmap文件向游戏中注入dll 前言 这篇文章仅记录我作为初学者使用FModel工具提取某款游戏模型的过程。 FModel简介 FModel是一个开源软件&#xff0c;可以用于查看…...

Qt C++ 屏幕录制 保存mp4

在麒麟系统&#xff08;基于 Linux&#xff09;上优化 Qt C 的屏幕录制&#xff0c;主要针对捕获效率和编码速度。可以参考以下优化策略&#xff1a; 1. 使用更高效的屏幕捕获 API 麒麟系统作为 Linux 系统的一种&#xff0c;可以考虑直接使用 X11、Wayland、或 DRM/KMS API …...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》

引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

pam_env.so模块配置解析

在PAM&#xff08;Pluggable Authentication Modules&#xff09;配置中&#xff0c; /etc/pam.d/su 文件相关配置含义如下&#xff1a; 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块&#xff0c;负责验证用户身份&am…...

基于当前项目通过npm包形式暴露公共组件

1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹&#xff0c;并新增内容 3.创建package文件夹...

基础测试工具使用经验

背景 vtune&#xff0c;perf, nsight system等基础测试工具&#xff0c;都是用过的&#xff0c;但是没有记录&#xff0c;都逐渐忘了。所以写这篇博客总结记录一下&#xff0c;只要以后发现新的用法&#xff0c;就记得来编辑补充一下 perf 比较基础的用法&#xff1a; 先改这…...

CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云

目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

【Java学习笔记】BigInteger 和 BigDecimal 类

BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点&#xff1a;传参类型必须是类对象 一、BigInteger 1. 作用&#xff1a;适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程&#xff1a;如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket&#xff08;服务端和客户端都要&#xff09;2. 绑定本地地址和端口&#x…...

uniapp手机号一键登录保姆级教程(包含前端和后端)

目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号&#xff08;第三种&#xff09;后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...

根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要

根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分&#xff1a; 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...