万字详解JVM,让你一文吃透( 五 )


基于栈的指令集的优缺点?优点:

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

万字详解JVM,让你一文吃透

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

文章插图
为什么要采用分层编译?因为即时编译器编译本地代码需要占用程序运行时间 , 要编译出优化程度更高的代码 , 所花费的时间越长 。
分层编译器有哪些层次?分层编译根据编译器编译、优化的规模和耗时,划分不同的编译层次,包括:
  • 第0层:程序解释执行,解释器不开启性能监控功能,可出发第1层编译 。
  • 第1层:也成为C1编译,将字节码编译为本地代码,进行简单可靠的优化,如有必要加入性能监控的逻辑 。
  • 第2层:也成为C2编译,也是将字节码编译为本地代码,但是会启用一些编译耗时较长的优化 , 甚至会根据性能监控信息进行一些不可靠的激进优化 。
用Client Compiler和Server Compiler将会同时工作 。用Client Compiler获取更高的编译速度,用Server Compiler获取更好的编译质量 。
编译对象与触发条件热点代码有哪些?
  • 被多次调用的方法
  • 被多次执行的循环体
如何判断一段代码是不是热点代码?要知道一段代码是不是热点代码,是不是需要触发即时编译,这个行为称为热点探测 。主要有两种方法:
  • 基于采样的热点探测,虚拟机周期性检查各个线程的栈顶 , 如果发现某个方法经常出现在栈顶,那这个方法就是“热点方法” 。实现简单高效,但是很难精确确认一个方法的热度 。
  • 基于计数器的热点探测,虚拟机会为每个方法建立计数器,统计方法的执行次数,如果执行次数超过一定的阈值 , 就认为它是热点方法 。
HotSpot虚拟机使用第二种,有两个计数器:
  • 方法调用计数器
  • 回边计数器(判断循环代码)
方法调用计数器统计方法统计的是一个相对的执行频率,即一段时间内方法被调用的次数 。当超过一定的时间限度,如果方法的调用次数仍然不足以让它提交给即时编译器编译,那这个方法的调用计数器就会被减少一半,这个过程称为方法调用计数器的热度衰减,这个时间就被称为半衰周期 。
有哪些经典的优化技术(即时编译器)?
  • 语言无关的经典优化技术之一:公共子表达式消除
  • 语言相关的经典优化技术之一:数组范围检查消除
  • 最重要的优化技术之一:方法内联
  • 最前沿的优化技术之一:逃逸分析
公共子表达式消除普遍应用于各种编译器的经典优化技术,它的含义是:
如果一个表达式E已经被计算过了,并且从先前的计算到现在E中所有变量的值都没有发生变化,那么E的这次出现就成了公共子表达式 。没有必要重新计算 , 直接用结果代替E就可以了 。

推荐阅读