Java虚拟机运行期优化

概述

为了提高热点代码的执行效率,在运行时,虚拟机将会把这些代码编译成与本地平台相关的机器码,并进行各种层次的优化,完成这个任务的编译器称为即时编译器(Just In TimeCompiler,下文中简称JIT编译器)。

解释器与编译器

解释器与编译器两者各有优势:当程序需要迅速启动和执行的时候,解释器可以首先发挥作用,省去编译的时间,立即执行。在程序运行后,随着时间的推移,编译器逐渐发挥作用,把越来越多的代码编译成本地代码之后,可以获取更高的执行效率。当程序运行环境中内存资源限制较大(如部分嵌入式系统中),可以使用解释执行节约内存,反之可以使用编译执行来提升效率。

 

为了在程序启动响应速度与运行效率之间达到最佳平衡,HotSpot虚拟机还会逐渐启用分层编译(Tiered Compilation)[4]的策略,分层编译的概念在JDK 1.6时期出现,后来一直处于改进阶段,最终在JDK 1.7的Server模式虚拟机中作为默认编译策略被开启。分层编译根据编译器编译、优化的规模与耗时,划分出不同的编译层次,其中包括:

  1. 第0层,程序解释执行,解释器不开启性能监控功能(Profiling),可触发第1层编译。
  2. 第1层,也称为C1编译,将字节码编译为本地代码,进行简单、可靠的优化,如有必要将加入性能监控的逻辑。
  3. 第2层(或2层以上),也称为C2编译,也是将字节码编译为本地代码,但是会启用一些编译耗时较长的优化,甚至会根据性能监控信息进行一些不可靠的激进优化。实施分层编译后,Client Compiler和Server Compiler将会同时工作,许多代码都可能会被多次编译,用Client Compiler获取更高的编译速度,用Server Compiler来获取更好的编译质量,在解释执行的时候也无须再承担收集性能监控信息的任务。

编译对象与触发条件

在运行过程中会被即时编译器编译的“热点代码”有两类,即:

  1. 被多次调用的方法。
  2. 被多次执行的循环体。
  3.  

判断一段代码是不是热点代码,是不是需要触发即时编译,这样的行为称为热点探测(Hot SpotDetection):

  1. 基于采样的热点探测(Sample Based Hot Spot Detection):
  2. 基于计数器的热点探测(Counter Based Hot Spot Detection):

编译优化技术

Java程序员有一个共识,以编译方式执行本地代码比解释方式更快,之所以有这样的共识,除去虚拟机解释执行字节码时额外消耗时间的原因外,还有一个很重要的原因就是虚拟机设计团队几乎把对代码的所有优化措施都集中在了即时编译器之中(在JDK 1.3之后,Javac就去除了-O选项,不会生成任何字节码级别的优化代码了),因此一般来说,即时编译器产生的本地代码会比Javac产生的字节码更加优秀。

方法内联的重要性要高于其他优化措施,它的主要目的有两个,一是去除方法调用的成本(如建立栈帧等),二是为其他优化建立良好的基础,方法内联膨胀之后可以便于在更大范围上采取后续的优化手段,从而获取更好的优化效果。

  1. 第一步:方法内联
  2. 第二步进行冗余访问消除
  3. 第三步我们进行复写传播
  4. 第四步我们进行无用代码消除

Java与C/C++的编译器对比

大多数程序员都认为C/C++会比Java语言快,甚至觉得从Java语言诞生以来“执行速度缓慢”的帽子就应当扣在它的头顶,这种观点的出现是由于Java刚出现的时候即时编译技术还不成熟,主要靠解释器执行的Java语言性能确实比较低下。但目前即时编译技术已经十分成熟,Java语言有可能在速度上与C/C++一争高下吗?

Java与C/C++的编译器对比实际上代表了最经典的即时编译器与静态编译器的对比,很大程度上也决定了Java与C/C++的性能对比的结果,因为无论是C/C++还是Java代码,最终编译之后被机器执行的都是本地机器码,哪种语言的性能更高,除了它们自身的API库实现得好坏以外,其余的比较就成了一场“拼编译器”和“拼输出代码质量”的游戏。

Java虚拟机的即时编译器与C/C++的静态优化编译器相比,产生劣势的原因:

  1. 因为即时编译器运行占用的是用户程序的运行时间,具有很大的时间压力
  2. Java语言是动态的类型安全语言,这就意味着需要由虚拟机来确保程序不会违反语言语义或访问非结构化内存。这就意味着虚拟机必须频繁地进行动态检查。对于这类程序代码没有明确写出的检查行为,尽管编译器会努力进行优化,但是总体上仍然要消耗不少的运行时间。
  3. Java语言中虽然没有virtual关键字,但是使用虚方法的频率却远远大于C/C++语言,这意味着运行时对方法接收者进行多态选择的频率要远远大于C/C++语言,也意味着即时编译器在进行一些优化(如前面提到的方法内联)时的难度要远大于C/C++的静态优化编译器。
  4. Java语言是可以动态扩展的语言,运行时加载新的类可能改变程序类型的继承关系,这使得很多全局的优化都难以进行
  5. java语言中对象的内存分配都是堆上进行的,只有方法中的局部变量才能在栈上分配[2]。而C/C++的对象则有多种内存分配方式,既可能在堆上分配,又可能在栈上分配,如果可以在栈上分配线程私有的对象,将减轻内存回收的压力。

 

java相较于c++的优势:

Java语言的这些性能上的劣势都是为了换取开发效率上的优势而付出的代价,动态安全、动态扩展、垃圾回收这些“拖后腿”的特性都为Java语言的开发效率做出了很大贡献。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值