1.什么是jvm
java Virtual Machine 中文译名:java虚拟机
JVM本质上是一个运行在计算机上的程序,他的职责是运行java字节码文件
主要是 .java文件 通过 javac命令编译为 .class(字节码文件)文件 进而通过java命令 运行代码
1.1 jvm的三大核心功能
- 解释和运行 :对字节码文件中的指令,实时的解释成机器让机器运行
- 内存管理:自动为对象、方法等分配内存,自动的垃圾回收机制
- 即时编译:对热点代码进行优化,提升执行效率
java的即时编译,主要是为了支持跨平台的特性
对相同的字节码文件,通过即时编译,可以实现在不同的平台上生成不同的机器码
1.2 即时编译
jvm提供了即时编译 JIT 进行性能的优化,最终能达到接近c、c++的运行性能甚至在特定的情况下实现超越
1.2.1 常见的JVM
名称 | 作者 | 支持版本 | 社区活跃度 | 特性 | 适用场景 |
HotSpot (Oracle JDK) | Oracle | 所有版本 | 高(闭源) | 使用最广泛,稳定可靠,社区活跃,JIT支持 Oracle JDK默认虚拟机 | 默认 |
HotSpot (Open JDK) | Oracle | 所有版本 | 中 | 同上 开源,OpenJDK默认虚拟机 | 默认 对jdk有二次开发需求 |
GraalVM | Oracle | 11,17,19 企业版支持8 | 高 | 多语言支持Ruby、python、C++等 高性能、JIT、AOT支持 | 微服务、云原生架构,需要多语言混合编程 |
Dragonwell JDK 龙井 | Alibaba | 标准版 8,11,17 扩展版 11,17 | 低 | 基于Open Jdk的增强 高性能、bug修复、安全性提升 JWarmup、ElasticHeap、Wisp特性支持 | 电商、物流、金融领域对性能要求比较高 |
Eclipse Openj9 (原IBM j9) | IBM | 8,11,17,19,20 | 低 | 高性能、可扩展 JIT、AOT特性支持 | 微服务、云原生架构 |
1.2.2 java虚拟机规范:
- 《Java虚拟机规范》由Oracle指定,内容主要包含了java虚拟机在设计和实现时需要遵守的规范,主要包含class字节码文件的定义、类和接口的加载和初始化、指令集等内容
- 《Java虚拟机规范》是对虚拟机设计的要求,而不是对java设计的要求,也就是说虚拟机可以运行在其他的语言,比如Groovy、Scala生成的class字节码文件之上
1.2.3 HotSpot的发展历程
1.3 字节码文件详解
jvm的组成
1.4 字节码文件的组成
1.4.1 应用场景:
int i=0;i=i++; //最后i等于多少? //java的反射如何实现的?
1.4.2 解决工作中的实际问题:
1. 版本冲突
2. 系统升级
1.4.3 字节码的正确打开方式
字节码文件中保存了源代码编译之后的内容,以二进制的方式存储,无法直接用记事本打开阅读
推荐使用工具 工具查看字节码文件
1.4.4 字节码文件的组成
1.4.4.1 基本信息
Magic 魔数——字节码文件前四个字节是由人为生成的
- 文件是无法通过文件扩展名来确定文件类型的,文件扩展名可以随意修改,不影响文件内容
- 软件使用文件的头几个字节(头文件)去校验文件的类型,如果软件不支持该种类类型就会出错
- JAVA字节码文件中,将文件头成为magic魔数
文件类型 | 字节数 | 文件头 |
JPEG(jpg) | 3 | FFDBFF |
PNG(png) | 4 | 89504E47(文件尾也有要求) |
bmp | 2 | 424D |
XML(xml) | 5 | 3C3F786D6C |
AVI() | 4 | 41564920 |
java字节码文件 | 4 | CAFEBABE |
主副版本号
- 主副版本号指的是编译字节码文件的JDK版本号,主版本号用来标识大版本号,JDK1.0-1.1使用了45.0-45.3,JDK1.2是46之后每升级一个大版本就加1;
- 副版本号是当主版本号相同时作为区分不同版本的标识,一般只需要关心主版本号。
- 版本号的作用主要是判断当前字节码的版本和运行时的jdk是否兼容
解决方案:
1. 升级jdk版本
2. 将第三方依赖的版本号降低或者更换依赖,以满足jdk版本的要求
访问标识
标识是类还是接口、注解、枚举、模块
标识public final abstract
1.4.4.2 常量池
- 字节码文件中的常量池的作用:避免相同的内容重复定义,节省空间
- 常量池中的数据都有一个编号,编号从1开始。在字段或者字节码指令中通过编号可以快速的找到对应的数据。
- 字节码指令通过编号引用到常量池的过程称之为符号引用
1.4.4.3 方法
一个非常有意思的面试题:int i=0;i=i++; 最终i的值是多少 最终结果为 i=0.
- 字节码中的方法区域是存放字节码指令的核心位置,字节码指令的额内容存放在方法的code属性中
int i=0;
int j=i+1;
由于代码直接对局部变量表进行累加,而后将操作数栈中的值存放到局部变量表中,操作数栈中数据没有进行操作;故最终结果为0;
1.4.5 玩转字节码的常用工具
1.4.5.1 javap -v命令
输入javap -v 字节码文件名称 查看具体的字节码信息 。(如果是jar包需要先使用jar -xvf 命令解压)
1.4.5.2 通过jclasslib 查看字节码
1.4.5.3 使用arthas
-
Arthas是一款线上监控诊断工具,通过全局视角实时查看应用load、内存、gc、线程的状态信息,并能在不修改代码的情况下,对业务问题进行诊断,大大提升线上问题排查效率
-
官网 简介 | arthas
arthas阿尔萨斯指令入门学习
-
dashboard 显示系统的实时数据面板
参数名称 | 参数说明 |
i | 刷新实时数据的时间间隔(ms),默认5000ms |
n | 刷新实时数据的次数 |
-
dump 将jvm中运行的字节码文件复制到指定目录下,适用场景:批量下载指定包目录的class字节码
参数名称 | 参数说明 |
class-pattern | 类名表达式匹配 |
[c:] | 类所属 ClassLoader 的 hashcode |
[classLoaderClass:] | 指定执行表达式的 ClassLoader 的 class name |
[d:] | 设置类文件的目标目录 |
[E] | 开启正则表达式匹配,默认为通配符匹配 |
-
jad 将jvm中实际运行的class的byte code 反编译成 .java 代码,便于你理解业务逻辑;如需批量下载指定包的目录的class字节码文件可以参考dump
参数名称 | 参数说明 |
class-pattern | 类名表达式匹配 |
[c:] | 类所属 ClassLoader 的 hashcode |
[classLoaderClass:] | 指定执行表达式的 ClassLoader 的 class name |
[E] | 开启正则表达式匹配,默认为通配符匹配 |
案例分析:使用阿里arthas定位线上出现的字节码问题
背景:小李的团队昨天对系统进行了升级修复了某个bug,但是升级完之后发现bug还是存在,小李怀疑是因为没有把最新的字节码文件部署到服务器上,请使用阿里的arthas去确认升级完的字节码文件是不是最新的。
解决方案:
- 在产生问题系统的服务器上面部署Arthas
- jad命令反编译获取java文件
- 查看java代码是否时更改后的版本