字节码技术
1、类文件结构
ca fe ba be 00 00 00 34 00 23 0a 00 06 00 15 09
- 前4个字节为魔数:cafababe
- 下来2个字节为小版本号:00 00
- 下来2个字节为大版本号:00 34 —> 52 (对应jdk1.8, 45为jdk1.1)
2、字节码指令
使用 javap 工具反编译 class 文件:
javap -v D:Demo.class
3、方法执行流程
- 虚拟机栈:方法局部变量表+操作数栈+动态连接+返回地址
- 局部变量表以slot为最小单位,存放方法参数和局部变量
4、异常处理
finally 中的字节码被复制了 3 份,分别放入 try 流程,catch 流程以及 catch 剩余的异常类型流程。
注意:虽然从字节码指令看来,每个块中都有 finally 块,但是 finally 块中的代码只会被执行一次
public class Code_18_FinallyReturnTest {
public static void main(String[] args) {
int i = Code_18_FinallyReturnTest.test();
// 结果为 20
System.out.println(i);
}
public static int test() {
int i;
try {
i = 10;
return i;
} finally {
i = 20;
return i;
}
}
}
- 如果在 finally 中出现了 return,会吞掉异常
- finally 中有return,会覆盖try中的return
- 字节码将异常信息显示在异常表中
5、语法糖
-
默认构造器
-
自动拆装箱(jdk1.5之前不能自动)
-
泛型擦除
-
可变参数(public static void foo(String… args){})
-
foreach()自动转换为for(i=0; i<n;i++)或集合的迭代器Iterator遍历
-
switch
-
try-with-resources(省去在finally中关闭资源,且能保留关闭资源时的异常)
-
方法重写时的桥接方法(子类返回值可以是父类返回值的子类–>jvm自动转换)
-
匿名内部类(自动生成一个类)
运行期优化
1)即时编译
HotSpot是解释器与编译器并存的:
-
对于大部分的代码采取解释执行的方式运行;
-
对于小部分运行频繁的热点代码,将其编译成机器码提高执行效率,以达到理想的运行速度。
分层编译
JVM 将执行状态分成了 5 个层次:
0层:解释执行,用解释器将字节码翻译为机器码
1层:使用 C1 即时编译器编译执行(不带 profiling)
2层:使用 C1 即时编译器编译执行(带基本的profiling)
3层:使用 C1 即时编译器编译执行(带完全的profiling)
4层:使用 C2 即时编译器编译执行
- 解释器:
- 将字节码解释为机器码,下次即使遇到相同的字节码,仍会执行重复的解释
- 将字节码解释为针对所有平台都通用的机器码
- 即时编译器
- 将一些字节码编译为机器码,并存入 Code Cache,下次遇到相同的代码,直接执行,无需再编译
- 根据平台类型,生成平台特定的机器码
执行效率上简单比较一下 Interpreter < C1(Client) < C2(Server)。
2)逃逸分析(Escape Analysis)
Java Hotspot 虚拟机分析新创建对象的动态作用域,并决定是否在 Java 堆上分配内存的一项技术。
当分析该对象不能逃逸到方法或线程外,进行以下优化:
- 栈上分配(内存直接分配在栈上,对象随栈帧出栈销毁)
- 同步消除
- 标量替换(不创建对象,只创建用到的成员变量)
3)方法内联
如果JVM监测到一些小方法被频繁的执行,它会把方法的调用替换成方法体本身,如:
private int add4(int x1, int x2, int x3, int x4) {
//这里调用了add2方法
return add2(x1, x2) + add2(x3, x4);
}
private int add2(int x1, int x2) {
return x1 + x2;
}
方法调用被替换后
private int add4(int x1, int x2, int x3, int x4) {
//被替换为了方法本身
return x1 + x2 + x3 + x4;
}