【Java】JVM字节码分析
一、功能
1、工作原理

Java编写的程序必须要先结果编译,但是这个步骤并不会生成特定平台的机器码。而是会生成一种平台无关性的.class文件。再由Java虚拟机来解释这个.class文件生成与平台相关的机器码,执行这个生成的机器码。达到跨平台的目的。
2、解释和运行
jvm本质上是运行在计算机上的程序,负责运行java字节码文件。
对字节码文件中的指令,实时的解释成机器码,计算机执行生成的机器码。
所以说,Java是编译与解释型语言,也可以说是不完全编译型语言。因为必须要先经过编译,JVM才能解释成字节码。
3、内存管理
自动为对象、方法等分配内存。自动垃圾回收机制,回收不再使用的对象。垃圾回收器会定期检查不再使用的对象,并回收它们占用的内存空间。JVM通过垃圾回收器来管理堆内存中的对象分配和释放。
4、 即时编译
在Java中每次执行都需要实时解释字节码文件成机器码,导致效率较低、速度变慢。这么做的原因是因为需要跨平台,不同操作系统的Java虚拟机不同,解释的也不一样,不同的虚拟机会转成当前操作系统的机器码。在不同的平台上JVM是不一样的,解释的机器码自然也是不一样的。
即时编译为了解决这个性能问题。JVM会识别热点代码(短时间多次调用), 会主动优化并且解释成机器码 ,将这个机器码保存在内存中。下次如果调用这段热点代码会直接从内存中取出调用。这样就省略了一次解释的步骤。这样在某些情况下性能就会提升很大 。
二、解释字节码
使用工具jclasslib工具查看class字节码

GitHub地址:https://github.com/ingokegel/jclasslib
1、分析class文件
public class HelloWorld {
public static void main(String[] args) {
int i = 0 ;
int j = i+1;
System.out.println(j);
}
}
对应的.class字节文件为
| 0 iconst_0 | 将常量0放入操作数栈中 |
|---|---|
| 1 istore_1 | 将操作数栈顶的数值存储到局部变量表1的位置 |
| 2 iload_1 | 将局部变量表1中的数复制到栈上 |
| 3 iconst_1 | 将常量1放入操作数栈中栈中 |
| 4 iadd | 将栈中最上面两个值进行相加,存储到栈顶 |
| 5 istore_2 | 从栈中取出操作数放入局部变量表中2号位置 |
| 13 return | 方法结束 |

2、分析i++
public class HelloWorld {
public static void main(String[] args) {
int i = 0 ;
i = i++;
System.out.println(i);
}
}
| 0 iconst_0 | 将常量0放入操作数栈中 |
|---|---|
| 1 istore_1 | 将操作数栈顶的数值存储到局部变量表1的位置 |
| 2 iload_1 | 将局部变量表1中的数复制到栈上 |
| 3 iich 1 by 1 | 将布局变量表1的位置的值加1 |
| 6 istore_1 | 将栈顶元素取出存储到局部变量表1的位置 |
| 10 iload_1 | 将布局变量表1位置的操作数复制到操作数栈 |
| 4 return | 从栈中取出操作数放入局部变量表中2号位置 |
| 13 return | 方法结束 |
这里由于2 iload_1指令比 3 iich 1 by 1 先执行,导致复制到操作数栈上的操作数没能加上,并且最后将其取出,导致加1的值被覆盖,所以最后的值为0,结果输出为0
3、分析++i
public class HelloWorld {
public static void main(String[] args) {
int i = 0 ;
i = ++i;
System.out.println(i);
}
}
| 0 iconst_0 | 将常量0放入操作数栈中 |
|---|---|
| 1 istore_1 | 将操作数栈顶的数值存储到局部变量表1的位置 |
| 2 iinc 1 by 1 | 将布局变量表1的位置的值加1 |
| 5 iload_1 | 将布局变量表1位置的操作数复制到操作数栈 |
| 6 istore_1 | 将栈顶元素取出存储到局部变量表1的位置 |
| 10 iload_1 | 将布局变量表1位置的操作数复制到操作数栈 |
| 14 return | 方法结束 |
相比于之前来说, iinc 1 by 1在iload_1 之前,先加1再复制到操作数栈中。所以取出来覆盖掉也是加1后的值。输出结果为1
三、总结
由于最后运行的是生成的机器码,而机器码是由JVM解释而来,那么排除代码层面的优化,我们可以查看编译成的.class文件中的字节指令,可以详细查找出代码中可能出现的问题,以及代码运行流程,快速的找出性能瓶颈。

Java 字节码是Java源代码编译生成的中间形式,赋予Java跨平台特性,通过对字节码的分析,可以迅速找到代码的性能瓶颈。
浙公网安备 33010602011771号