【JVM】关于JVM,你需要掌握这些 | 一文彻底吃透JVM系列( 六 )


解释执行当主流的虚拟机中都包含了即时编译器后 , Class文件中的代码到底会被解释执行还是编译执行 , 只有虚拟机自己才能准确判断 。
Javac编译器完成了程序代码经过词法分析、语法分析到抽象语法树 , 再遍历语法树生成线性的字节码指令流的过程 。因为这一动作是在Java虚拟机之外进行的 , 而解释器在虚拟机的内部 , 所以Java程序的编译是半独立的实现 。
基于栈的指令集和基于寄存器的指令集什么是基于栈的指令集?Java编译器输出的指令流 , 里面的指令大部分都是零地址指令 , 它们依赖操作数栈进行工作 。
计算“1+1=2” , 基于栈的指令集是这样的:
iconst_1iconst_1iaddistore_0两条iconst_1指令连续地把两个常量1压入栈中 , iadd指令把栈顶的两个值出栈相加 , 把结果放回栈顶 , 最后istore_0把栈顶的值放到局部变量表的第0个Slot中 。
什么是基于寄存器的指令集?最典型的是x86的地址指令集 , 依赖寄存器工作 。计算“1+1=2” , 基于寄存器的指令集是这样的:
mov eax,1add eax,1mov指令把EAX寄存器的值设为1 , 然后add指令再把这个值加1 , 结果就保存在EAX寄存器里 。
基于栈的指令集的优缺点?优点:

  • 可移植性好:用户程序不会直接用到这些寄存器 , 由虚拟机自行决定把一些访问最频繁的数据(程序计数器、栈顶缓存)放到寄存器以获取更好的性能 。
  • 代码相对紧凑:字节码中每个字节就对应一条指令
  • 编译器实现简单:不需要考虑空间分配问题 , 所需空间都在栈上操作
缺点:
  • 执行速度稍慢
  • 完成相同功能所需的指令熟练多
频繁的访问栈 , 意味着频繁的访问内存 , 相对于处理器 , 内存才是执行速度的瓶颈 。
Javac编译过程分为哪些步骤?
  1. 解析与填充符号表
  2. 插入式注解处理器的注解处理
  3. 分析与字节码生成

【JVM】关于JVM,你需要掌握这些 | 一文彻底吃透JVM系列

文章插图
什么是即时编译器?Java程序最初是通过解释器进行解释执行的 , 当虚拟机发现某个方法或代码块的运行特别频繁 , 就会把这些代码认定为“热点代码”(Hot Spot Code) 。
为了提高热点代码的执行效率 , 在运行时 , 虚拟机将会把这些代码编译成与本地平台相关的机器码 , 并进行各种层次的优化 , 完成这个任务的编译器成为即时编译器(Just In Time Compiler , JIT编译器) 。
解释器和编译器许多主流的商用虚拟机 , 都同时包含解释器和编译器 。
  • 当程序需要快速启动和执行时 , 解释器首先发挥作用 , 省去编译的时间 , 立即执行 。
  • 当程序运行后 , 随着时间的推移 , 编译器逐渐发挥作用 , 把越来越多的代码编译成本地代码 , 可以提高执行效率 。
如果内存资源限制较大(部分嵌入式系统) , 可以使用解释执行节约内存 , 反之可以使用编译执行来提升效率 。同时编译器的代码还能退回成解释器的代码 。
【JVM】关于JVM,你需要掌握这些 | 一文彻底吃透JVM系列

文章插图
为什么要采用分层编译?因为即时编译器编译本地代码需要占用程序运行时间 , 要编译出优化程度更高的代码 , 所花费的时间越长 。

经验总结扩展阅读