JVM-字节码文件的组成

Java虚拟机的组成

Java虚拟机主要分为以下几个组成部分:

  • 类加载子系统:核心组件类加载器,负责将字节码文件中的内容加载到内存中。
  • 运行时数据区:JVM管理的内存,创建出来的对象、类的信息等等内容都会放在这块区域中。
  • 执行引擎:包含了即时编译器、解释器、垃圾回收器,执行引擎使用解释器将字节码指令解释成机器码,使用即时编译器优化性能,使用垃圾回收器回收不再使用的对象。
  • 本地接口:调用本地使用C/C++编译好的方法,本地方法在Java中声明时,都会带上native关键字,如下图所示。

正确打开字节码文件

推荐使用 jclasslib工具查看字节码文件。Github地址: https://github.com/ingokegel/jclasslib

字节码文件总共可以分为以下几个部分:

  • 基础信息:魔数、字节码文件对应的Java版本号、访问标识(public final等等)、父类和接口信息
  • 常量池: 保存了字符串常量、类或接口名、字段名,主要在字节码指令中使用
  • 字段: 当前类或接口声明的字段信息
  • 方法: 当前类或接口声明的方法信息,核心内容为方法的字节码指令
  • 属性: 类的属性,比如源码的文件名、内部类的列表等

字节码基本信息

基本信息包含了jclasslib中能看到的两块内容: 

 Magic魔数

每个Java字节码文件的前四个字节是固定的,用16进制表示就是0xcafebabe。文件是无法通过文件扩展名来确定文件类型的,文件扩展名可以随意修改不影响文件的内容。软件会使用文件的头几个字节(文件头)去校验文件的类型,如果软件不支持该种类型就会出错。Java字节码文件中,将文件头称为magic魔数。Java虚拟机会校验字节码文件的前四个字节是不是0xcafebabe,如果不是,该字节码文件就无法正常使用,Java虚拟机会抛出对应的错误。

比如常见的文件格式校验方式如下:

主副版本号

主副版本号指的是编译字节码文件时使用的JDK版本号,主版本号用来标识大版本号,JDK1.0-1.1使用了45.0-45.3,JDK1.2是46之后每升级一个大版本就加1;副版本号是当主版本号相同时作为区分不同版本的标识,一般只需要关心主版本号1.2之后大版本号计算方法就是 : 主版本号 – 44,比如主版本号52就是JDK8。

 版本号的作用主要是判断当前类文件当编译时产生字节码的版本和运行时的JDK是否兼容。如果使用较低版本的JDK去运行较高版本JDK的字节码文件,无法使用会显示如下错误:

有两种方案:

1.升级JDK版本,将图中使用的JDK6升级至JDK8即可正常运行,容易引发其他的兼容性问题,并且需要大量的测试。

2.将第三方依赖的版本号降低或者更换依赖,以满足JDK版本的要求。建议使用这种方案

 字节码常量池 

字节码文件中常量池的作用:避免相同的内容重复定义,节省空间。如下图,常量池中定义了一个字符串,字符串的字面量值为123。

比如在代码中,编写了两个相同的字符串“我爱北京天安门”,字节码文件甚至将来在内存中使用时其实只需要保存一份,此时就可以将这个字符串以及字符串里边包含的字面量,放入常量池中以达到节省空间的作用。
String str1 = "我爱北京天安门";
String str2 = "我爱北京天安门";

 属性名索引主要指向的字段的类型,常量值索引就是指向字符常量,这里面三个字段的常量值索引都指向abc这个字符串常量

 而指向的8号索引才最终指向27号真正的字符串,在后续的jvm的字符串常量池中会将8号这个string类型的索引入到StringTable中,故需要通过8号索引再找到27号索引,而不能在字段中直接指向27号索引这个字符串本身。

 如果我们将27号这个引用直接去掉,用8号引用直接存放这个字符串,不要27号这个引用的话,也是不可取的。因为我们定义的字段名可能与字符串名重复,这时候字段名就会直接指向字符串本身。如果没有这种间接引用,出现这种情况就会很难解决。

 常量池中的数据都有一个编号,编号从1开始。比如“我爱北京天安门”这个字符串,在常量池中的编号就是7。在字段或者字节码指令中通过编号7可以快速的找到这个字符串。

字节码指令中通过编号引用到常量池的过程称之为符号引用

方法区

字节码中的方法区域是存放字节码指令的核心位置,字节码指令的内容存放在方法的Code属性中。 

 通过分析方法的字节码指令,可以清楚地了解一个方法到底是如何执行的。先来看如下案例:

int i = 0;
int j = i + 1;

 这段代码编译成字节码指令之后是如下内容:

要理解这段字节码指令是如何执行的,我们需要先理解两块内存区域:操作数栈和局部变量表。

操作数栈是用来存放临时数据的内容,是一个栈式的结构,先进后出。

局部变量表是存放方法中的局部变量,包含方法的参数、方法中定义的局部变量,在编译期就已经可以确定方法有多少个局部变量。

1、iconst_0,将常量0放入操作数栈。此时栈上只有0。 

2、istore_1会从操作数栈中,将栈顶的元素弹出来,此时0会被弹出,放入局部变量表的1号位置。局部变量表中的1号位置,在编译时就已经确定是局部变量i使用的位置。完成了对局部变量i的赋值操作。 

 3、iload_1将局部变量表1号位置的数据放入操作数栈中,此时栈中会放入0。

 

4、iconst_1会将常量1放入操作数栈中。 

5、iadd会将操作数栈顶部的两个数据相加,现在操作数栈上有两个数0和1,相加之后结果为1放入操作数栈中,此时栈上只有一个数也就是相加的结果1。 

 6、istore_2从操作数栈中将1弹出,并放入局部变量表的2号位置,2号位置是j在使用。完成了对局部变量j的赋值操作。

7、return语句执行,方法结束并返回。

 

同理,我们可以自行分析下i++和++i的字节码指令执行的步骤。

i++的字节码指令如下,其中iinc 1 by 1指令指的是将局部变量表1号位置增加1,其实就实现了i++的操作。

而++i只是对两个字节码指令的顺序进行了更改:

 

面试题

问:int i = 0; i = i++; 最终i的值是多少?

答:答案是0,我通过分析字节码指令发现,i++先把0取出来放入临时的操作数栈中,

接下来在临时数据表中对i进行加1,i变成了1,最后再将之前保存的临时值0重新赋值到临时数据表中的i,最后i就变成了0。

 属性

属性主要指的是类的属性,比如源码的文件名、内部类的列表等。

 

 

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

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

相关文章

100.乐理基础-五线谱-是否需要学习五线谱

内容参考于:三分钟音乐社 上一个内容:99.乐理基础-简谱的多声部-CSDN博客 简谱与五线谱的区别,各自的优劣势、使用场景、范围等: 要搞懂这个问题,其实核心就是四个词:首调、固定调、单声部、多声部 首调、…

如何在Ubuntu安装配置SVN服务端并实现无公网ip访问内网资料库

🌈个人主页: Aileen_0v0 🔥热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​💫个人格言:“没有罗马,那就自己创造罗马~” 文章目录 前言1. Ubuntu安装SVN服务2. 修改配置文件2.1 修改svnserve.conf文件2.2 修改passwd文件2.3 修改au…

TypeScript(二) 基础类型

1. typescript的基础类型 数据类型关键字描述任意类型any生命any的变量可以赋值任意类型的值数字类型number整数或分数字符串类型string使用单引号(‘’)或者双引号(“”)来表示字符串类型。反引号()来定义…

前端 HTML 知识点之 HTML 介绍

1.1 HTML 1.1.1 定义 超文本标记语言(英语:HyperTextMarkupLanguage,简称:HTML)是一种用于创建网页的标准标记语言 HTML元素是构建网站的基石 标记语言(markup language ) 由无数个标记&#…

黑马程序员-瑞吉外卖-day5

修改实体类 package com.itheima.reggie.entity;import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.TableField; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode;i…

kali系统入侵电脑windows(win11系统)渗透测试,骇入电脑教学

本次渗透测试将使用kali虚拟机(攻击机)对本机(靶机)进行入侵并监控屏幕 声明:本篇仅仅是将本机作为靶机的一次简易渗透测试,实际情况中基本不可能出现如此简单的木马骇入(往往在上传木马时就被防…

【Linux】第三十八站:信号处理

文章目录 一、信号处理二、再谈进程地址空间三、内核如何实现信号的捕捉四、sigaction 一、信号处理 我们知道,信号保存以后,会在合适的时候进行处理这个信号。 那么信号是如何被处理的?什么时候进行处理呢? 当我们的进程从内核…

破解Windows系统密码(保姆级教学)

前言: 本篇博客只是技术分享并非非法传播知识,实验内容均是在虚拟机中进行,并非真实环境 正文: 看到题目大家都已经晓得这篇博客是干嘛了,我也不废话了,直接上win7素材 需要windows10破解过程的关注后在下面评论"已关注,请私聊"我会私发给你 一.windows7电脑密码破解…

分布式空间索引了解与扩展

目录 一、空间索引快速理解 (一)区域编码 (二)区域编码检索 (三)Geohash 编码 (四)RTree及其变体 二、业内方案选取 三、分布式空间索引架构 (一)PG数…

DCNv4:对视觉应用的动态和稀疏算子的重新思考

摘要 https://arxiv.org/pdf/2401.06197.pdf 我们介绍了可变形卷积v4(DCNv4),这是一种高效且有效的运算符,专为广泛的视觉应用而设计。DCNv4解决了其前身DCNv3的局限性,通过两个关键改进:1. 去除空间聚合中…

Springmvc-@RequestBody

SpringBoot-2.7.12 请求的body参数无法转换,服务端没有报错信息打印,而是响应的状态码是400 PostMapping("/static/user") public User userInfo(RequestBody(required false) User user){user.setAge(19);return user; }PostMapping("…

Vue学习笔记之应用创建和基础知识

1、安装方式 CDN方式安装&#xff1a; <script src"https://unpkg.com/vue3/dist/vue.global.js"></script> 2、创建应用 使用Vue内置对象创建一个应用&#xff0c;基本代码结构如下&#xff1a; <script src"https://unpkg.com/vue3/dist/…

无代理快速访问github

无代理快速访问github 修改hosts文件&#xff0c;添加以下内容 140.82.112.3 github.com 199.232.69.194 github.global.ssl.fastly.net

嵌入式驱动开发需要会哪些技能?

嵌入式驱动开发是指在嵌入式系统中编写驱动程序&#xff0c;实现设备与计算机之间的通信。嵌入式驱动开发是指编写设备驱动程序&#xff0c;实现设备与计算机之间的通信。以下是一些嵌入式驱动开发的具体操作方法: 1&#xff09;了解硬件设备结构&#xff1a;在进行嵌入式驱动…

修复idea,eclipse ,clion控制台中文乱码

控制台乱码问题主要原因并不在编译器IDE身上&#xff0c;还主要是Windows的控制台默认编码问题。。。 Powershell&#xff0c;cmd等默认编码可能不是UTF-8&#xff0c;无需改动IDE的settings或者properties&#xff08;这治标不治本&#xff09;&#xff0c;直接让Windows系统…

小土堆pytorch学习笔记003 | 下载数据集dataset 及报错处理

目录 1、下载数据集 2、展示数据集里面的内容 3、DataLoader 的使用 例子&#xff1a; 结果展示&#xff1a; 1、下载数据集 # 数据集import torchvisiontrain_set torchvision.datasets.CIFAR10(root"./test10_dataset", trainTrue, downloadTrue) test_set …

uniapp,页面当有按钮的时候,可以做一个动态的效果

效果&#xff1a; 这个是当点着按钮的时候没有松开按钮的效果&#xff08;没有阴影&#xff09; 这个是当松开按钮的效果&#xff08;有阴影&#xff09; 原理讲解&#xff1a; 这段代码实现的业务逻辑是在一个Vue组件中控制“现金”按钮的阴影效果。具体来说&#xff0c;它通…

网络爬虫的基本原理、应用场景及注意事项

基本原理&#xff1a; 发送HTTP请求&#xff1a;网络爬虫首先通过编程方式模拟用户浏览器行为&#xff0c;向目标网站发送HTTP/HTTPS请求&#xff0c;获取网页内容。这一过程通常利用Python中的requests库或者Java的URLConnection、HttpClient等工具来实现。解析响应数据&…

云上高可用系统-韧性设计模式

一、走近韧性设计模式 &#xff08;一&#xff09;基本概念 韧性设计模式是一系列在软件工程中用于提高系统韧性的设计原则、策略、实践和模式。韧性&#xff08;Resilience&#xff09;在这里指的是系统对于各种故障、异常和压力的抵抗能力&#xff0c;以及在遭受这些挑战后…

pnpm 源不对 Will retry in 10 seconds. 2 retries left.

现象 由于使用npm config set registry 切换淘宝源时&#xff0c;把地址打错了。 后面使用pnpm install 时出现 此时无论我怎么使用npm config set registry 或者pnpm config set registry 切回正确的源均没有效果。 解决 在其他用npm的项目运行一下npm i 再运行pnpm i 即…