(一)Java虚拟机——JVM的组成

什么是JVM?

JVM全称是 Java Virtual Machine,中文译名 Java虚拟机,是Java语言的核心组件,它是一个能够执行Java字节码的虚拟计算机。JVM的主要职责是允许Java程序在任何平台上运行,无需为每种硬件和操作系统重新编写代码,从而实现了Java的"一次编写,处处运行"的理念。通俗的说就是跨平台用的。

JVM的功能

  • 解释和运行:对字节码文件中的指令,实时解释为机器码,让计算机运行。
  • 内存管理:自动为对象、方法等分配内存自动的垃圾回收机制,回收不在使用的对象。
  • 即时编译:对热点代码进行优化,提升执行效率。

工作原理:

对比C/C++:因为Java的即时编译,性能不如C/C++(如果不做任何优化)

JDK,JRE和JVM三者的关系?

JDK:英文全称 Java Development Kit,是Java的开发工具包 JDK是提供给Java开发人员使用的,其中包含了Java的开发工具JRE。其中的开发工具包括:编译工具(javac.exe)打包工具(jre.exe)等。通俗的说就是开发用的

JRE:英文全称 Java Runtime Environment,是Java运行环境 JRE包括Java虚拟机 (JVM Java Virtual Machine)和Java程序所需的核心类库等,如果想要运行一个开发好的Java程序,计算机中只需要安装JRE即可。通俗的说就是运行用的

JDK = JRE + 开发工具集(例如Javac编译工具等)

JRE = JVM + Java SE 标准类库

JVM的组成

JVM主要是由类加载器、运行时数据区域、执行引擎、本地接口。

字节码文件的组成

字节码文件保存了源代码编译之后的内容,以二进制的方式存储

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

字节码文件的组成------基本信息

Magic魔数

说明:

  • 文件是无法通过文件拓展名来确定文件类型的,文件拓展名可以随意修改,不影响文件的内容。
  • 软件使用文件的头几个字节(文件头)来校验文件的类型,如果软件不支持改类型就会报错。

在Java字节码文件中,将文件头称为Magic魔数。

Java的字节码文件,字节数为4,文件头为:CAFFBABE

主副版号

主副版号是指编译字节码文件的JDK版号。主版号用来识别大版本,副版号用来识别不同的版本。

主要作用:判断当前字节码版本与运行时的JDK是否兼容。

判断版本:主版号 - 44

比如主版号52就是JDK8

思考:

如果遇到依赖版本与JDK版本不兼容,导致的报错,解决方案

  1. 修改JDK版本与该依赖兼容的版本。(不建议,容易引发其他文件的正常运行)
  2. 更换兼容该JDK版本的依赖版本。(推荐)

字节码文件的组成------常量池

作用:避免相同的内容重复定义,节省空间。

定义变量后,是先指向类型,由类型指向常量。

使用IDEA插件jclasslib查看字节码文件

在idea中搜索 jclasslib 并完成下载和安装

新建一个Java程序

java 复制代码
    public static void main(String[] args) {
        int i = 0;
        i = i++;
        System.out.println(i);
    }

注意: 运行后,才能看到字节码文件

运行后,选择 view -> show bytecode With Jclasslib

在弹出的框里面,选择 方法 --> main --> Code 即可看到字节码文件

点击一条语句,选择显示JVM规范,即可看到相关的解释

字节码文件的组成------方法

这里引入两个新概念--操作数栈和局部变量表

操作数栈:临时存放数据的地方

局部变量表:存放方法中的局部变量的位置

下面以:int i = 0; int j = i + 0 .为例子,讲解一下

使用jclasslib 查看这两句的 字节码文件

bash 复制代码
0 iconst_0
1 istore_1
2 iload_1
3 iconst_1
4 iadd
5 istore_2
6 return

逐一解析

  1. iconst_0 (索引0)

    • 作用 :将整型常量 0 压入操作数栈。

    • 操作数栈变化[空] → [0]

  2. istore_1 (索引1)

    • 作用 :将栈顶的整型值 0 存储到局部变量表的索引 1 的位置。

    • 局部变量表变化 :索引 1 的值变为 0

    • 操作数栈变化[0] → [空]

  3. iload_1 (索引2)

    • 作用 :从局部变量表索引 1 加载整型值 0 到操作数栈。

    • 操作数栈变化[空] → [0]

  4. iconst_1 (索引3)

    • 作用 :将整型常量 1 压入操作数栈。

    • 操作数栈变化[0] → [0, 1]

  5. iadd (索引4)

    • 作用 :弹出栈顶的两个值 01,相加后结果 1 压回栈顶。

    • 操作数栈变化[0, 1] → [1]

  6. istore_2 (索引5)

    • 作用 :将栈顶的整型值 1 存储到局部变量表索引 2 的位置。

    • 局部变量表变化 :索引 2 的值变为 1

    • 操作数栈变化[1] → [空]

  7. return (索引6)

    • 作用:结束当前方法(无返回值)。

面试题

int i = 0; i = i++ 问 i 等于多少

先看字节码文件

bash 复制代码
0 iconst_0
1 istore_1
2 iload_1
3 iinc 1 by 1
6 istore_1
7 return

从字节码文件中分析出++操作之间在局部变量中操作,不需要在操作数栈的中操作。

回答:答案是0,我通过分析字节码指令发现,i++先把0取出来放入临时的操作数栈中,
接下来对进行加1,i变成了1,最后再将之前保存的临时值0放入i,最后i就变成了0。

面试题二

问 int i = 0 , j = 0 ,k = 0 ; 比较: i ++; j = j + 1; k += 1; 的性能?

先查看每一个的字节码文件

i ++:

bash 复制代码
0 iconst_0
1 istore_1
2 iload_1
3 iinc 1 by 1
6 istore_1
7 return

j = j + 1:

bash 复制代码
0 iconst_0
1 istore_1
2 iload_1
3 iconst_1
4 iadd
5 istore_1
6 return

k += 1:

bash 复制代码
0 iconst_0
1 istore_1
2 iinc 1 by 1
5 return

由字节码文件可知:

  • 优先使用 i++k += 1:编译优化更高效,代码简洁且性能更好。

  • 避免 j = j + 1:冗余的栈操作会降低性能(尤其在循环中)

玩转字节码工具------arthas

官网:arthas (aliyun.com)

Arthas 是一款线上监控诊断产品,通过全局视角实时查看应用 load、内存、gc、线程的状态信息,并能在不修改应用代码的情况下,对业务问题进行诊断,包括查看方法调用的出入参、异常,监测方法执行耗时,类加载信息等,大大提升线上问题排查效率。

这里使用 arthas 官网自带的调试工具进行测试

开一个 cmd 窗口,运行以下命令。

math-game是一个简单的程序,每隔一秒生成一个随机数,再执行质因数分解,并打印出分解结果。

bash 复制代码
curl -O https://arthas.aliyun.com/math-game.jar
java -jar math-game.jar

再开一个窗口,运行以下命令

bash 复制代码
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar

之后就会看到我们刚才启动的程序 ,输入3,即可进入

查看 dashboard

输入

dashboard ,按回车/enter,会展示当前进程的信息,按ctrl+c可以中断执行。

也可以指定,多少毫秒刷新和刷新多少次

如果没有指定默认5000ms刷新,刷新无数次

bash 复制代码
dashboard -i 毫秒 -n 次数

此图可以查看CPU的占用情况,线程的运行时间等

memory:内存

runtime:运行时间内的相关配置信息

dump

已加载类的 字节码文件 到特定目录

bash 复制代码
dump -d 指定输出路径 包名.类名

以之前已经运行的程序为例

打开这个目录后,会发现一个MathGame.class的文件。使用JClassLib: JClassLib不但是一个字节码阅读器而且还包含一个类库允许开发者读取,修改,写入Java Class文件与字节码。 (gitee.com)

下载并安装这个工具打开,可以看到它的信息

jad

jad 命令将 JVM 中实际运行的 class 的 byte code 反编译成 java 代码,便于你理解业务逻辑;

bash 复制代码
jad 包名.类名
相关推荐
侠客行031713 小时前
Mybatis连接池实现及池化模式
java·mybatis·源码阅读
蛇皮划水怪13 小时前
深入浅出LangChain4J
java·langchain·llm
灰子学技术15 小时前
go response.Body.close()导致连接异常处理
开发语言·后端·golang
老毛肚15 小时前
MyBatis体系结构与工作原理 上篇
java·mybatis
风流倜傥唐伯虎15 小时前
Spring Boot Jar包生产级启停脚本
java·运维·spring boot
二十雨辰15 小时前
[python]-AI大模型
开发语言·人工智能·python
Yvonne爱编码16 小时前
JAVA数据结构 DAY6-栈和队列
java·开发语言·数据结构·python
Re.不晚16 小时前
JAVA进阶之路——无奖问答挑战1
java·开发语言
你这个代码我看不懂16 小时前
@ConditionalOnProperty不直接使用松绑定规则
java·开发语言
pas13616 小时前
41-parse的实现原理&有限状态机
开发语言·前端·javascript