1、字节码指令
1.1图解方法执行流程
1)原始 java 代码
/**
* 演示 字节码指令 和 操作数栈、常量池的关系
*/
public class Demo3_1 {
public static void main(String[] args) {
int a = 10;
int b = Short.MAX_VALUE + 1;
int c = a + b;
System.out.println(c);
}
}
2)编译后的字节码文件
3)常量池载入运行时常量池
4)方法字节码载入方法区
5)main 线程开始运行,分配栈帧内存
(stack=2,locals=4)
6)执行引擎开始执行字节码
上述过程很多步入栈出栈,仅展示一步示例
2、动态加载
2.1不同方法的inovke
构造方法、私有 invokespecial
静态方法用的是 invokestatic
公共方法 invokevirtual(不确定)
前两个效率更高,直接找到了方法加载地址
第三个运行时动态生成(可能重写)
2.2 多态方法查找过程
当执行 invokevirtual 指令时:
- 先通过栈帧中的对象引用找到对象
- 分析对象头,找到对象的实际 Class
- Class 结构中有 vtable,它在类加载的链接阶段就已经根据方法的重写规则生成好了
- 查表得到方法的具体地址
- 执行方法的字节码
3、编译器优化
A编译成字节码指令时,编译器做了许多优化
1)比如 finally时,只要不在里面return,它会自动加athrow,抛出异常。
2)在synchronized同步代码块时也是,有monitorentor和exit两个操作。lock对象复制成了两份,确保能够解锁。
方法级别的 synchronized 不会在字节码指令中有所体现
B语法糖
其实就是指 java 编译器把 *.java 源码编译为 *.class 字节码的过程中,自动生成和转换的一些代码,主要是为了减轻程序员的负担,是 java 编译器给我们的一个额外福利(给糖吃嘛)这个和lombook自动生成代码很像。
- 所以,那些反对lombook的人怎么看待语法糖呢?
3.1 默认构造器
3.2 自动拆装箱
3.3 泛型集合取值
3.4 可变参数
3.5 foreach 循环
3.6 switch 字符串
3.7 switch 枚举
3.8 枚举类
3.9 try-with-resources
3.10 方法重写时的桥接方法
3.11 匿名内部类
4、运行器优化
1即时编译
1.1 分层编译
1)JVM 将执行状态分成了 5 个层次:
0 层,解释执行(Interpreter)
1 层,使用 C1 即时编译器编译执行(不带 profiling)
2 层,使用 C1 即时编译器编译执行(带基本的 profiling)
3 层,使用 C1 即时编译器编译执行(带完全的 profiling)
4 层,使用 C2 即时编译器编译执行
2)即时编译器(JIT)与解释器的区别
jit:将字节码编译为机器码,并存入code cache。下次遇到相同的代码,直接执行,无需编译。
根据平台类型生成特定机器码
解释器:将字节码解释为机器码,下次仍会重复解释
解释为所有平台都通用的机器码。
1.2方法内联
热点方法会进行内联,所谓的内联就是把方法内代码拷贝、粘贴到调用者的位置。也会常量折叠。