JVM笔记 – 程序编译与代码优化(晚期(运行期)优化)
本文由发表于3年前 | J2EE | 暂无评论 |  被围观 2,473 views+
1、概述2、HotSpot虚拟机内的即时编译器2.1、解释器与编译器2.2、编译对象与触发条件2.3、编译过程2.4、查看及分析即时编译结果3、编译优化技术3.1、优化技术概览3.2、公共子表达式消除3.3、数组边界检查消除3.4、方法内联3.5、逃逸分析4、Java与C/C++的编译器对比5、本章小结
《深入理解Java虚拟机:JVM高级特性与最佳实践(第2版)》笔记
1、概述

即时编译器并不是虚拟机必需的部分。

本章提及的编译器、即时编译器都是指 HotSpot 虚拟机内的即时编译器,虚拟机也是特指 HotSpot 虚拟机。

2、HotSpot虚拟机内的即时编译器
2.1、解释器与编译器

HotSpot 虚拟机中内置了两个即时编译器,分别称为 Client Compiler 和 Server   Compiler。

HotSpot 虚拟机会根据自身版本与宿主机器的硬件性能自动选择运行模式,用户也可以使用"- client" 或"- server" 参数去强制指定虚拟机运行在 Client 模式或 Server 模式。

参数"- Xint" 强制虚拟机运行于“解释模式”( Interpreted   Mode)。

参数"- Xcomp" 强制虚拟机运行于“编译模式”( Compiled   Mode),这时将优先采用编译方式执行程序,但是解释器仍然要在编译无法进行的情况下介入执行过程。

为了在程序启动响应速度与运行效率之间达到最佳平衡, HotSpot 虚拟机还会逐渐启用分层编译( Tiered Compilation)[ 4] 的策略。

实施分层编译后, Client   Compiler 和 Server   Compiler 将会同时工作,许多代码都可能会被多次编译,用 Client   Compiler 获取更高的编译速度,用 Server   Compiler 来获取更好的编译质量,在解释执行的时候也无须再承担收集性能监控信息的任务。

2.2、编译对象与触发条件

“热点代码”有两类,即:被多次调用的方法。被多次执行的循环体。

这种编译方式因为编译发生在方法执行过程之中,因此形象地称之为栈上替换( On   Stack   Replacement, 简称为 OSR 编译,即方法栈帧还在栈上,方法就被替换了)。

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

目前主要的热点探测判定方式有两种:基于采样的热点探测,基于计数器的热点探测。

在 HotSpot 虚拟机中使用的是第二种——基于计数器的热点探测方法,因此它为每个方法准备了两类计数器:方法调用计数器( Invocation   Counter) 和回边计数器( Back   Edge   Counter)。

当计数器超过阈值溢出了,就会触发 JIT 编译。

当编译工作完成之后,这个方法的调用入口地址就会被系统自动改写成新的。

使用虚拟机参数- XX:- UseCounterDecay 来关闭热度衰减,让方法计数器统计方法调用的绝对次数。

使用- XX: CounterHalfLifeTime 参数设置半衰周期的时间,单位是秒。

回边计数器,它的作用是统计一个方法中循环体代码执行的次数。

建立回边计数器统计的目的就是为了触发 OSR 编译。

参数- XX: OnStackReplacePercentage 来间接调整回边计数器的阈值。

2.3、编译过程

在默认设置下,无论是方法调用产生的即时编译请求,还是 OSR 编译请求,虚拟机在代码编译器还未完成之前,都仍然将按照解释方式继续执行,而编译动作则在后台的编译线程中进行。

用户可以通过参数- XX:- BackgroundCompilation 来禁止后台编译。

对于 Client   Compiler 来说,它是一个简单快速的三段式编译器,主要的关注点在于局部性的优化,而放弃了许多耗时较长的全局优化手段。

而 Server Compiler 则是专门面向服务端的典型应用并为服务端的性能配置特别调整过的编译器,也是一个充分优化过的高级编译器,几乎能达到 GNU C++编译器使用-O2参数时的优化强度。

2.4、查看及分析即时编译结果
3、编译优化技术
3.1、优化技术概览

这些代码优化变换是建立在代码的某种中间表示或机器码之上,绝不是建立在Java源码之上的。

3.2、公共子表达式消除
3.3、数组边界检查消除

除了如数组边界检查优化这种尽可能把运行期检查提到编译器完成的思路之外,另外还有一种避免思路:隐式异常处理。

当 foo 不为空的时候,对 value 的访问是不会额外消耗一次对 foo 判空的开销的。代价就是当 foo 真的为空时,必须转入到异常处理器中恢复并抛出 NullPointException异常,这个过程必须从用户态转动内核态中处理,结束后再回到用户态,速度远比一次判空检查慢。

3.4、方法内联

只有使用invokespecial指令调用的私有方法、实例构造器、父类方法以及使用invokestatic指令进行调用的静态方法才是在编译期进行解析的。

3.5、逃逸分析

逃逸分析的基本行为就是分析对象动态作用域。

如果确定一个方法不会逃逸出方法之外,那让整个对象在栈上分配内存将会是一个很不错的主意,对象所占用的内存空间就可以随栈帧而销毁。在一般应用中,不会逃逸的局部对象所占用的比例很大,如果能使用栈上分配,那大量的对象就会随着方法结束而自动销毁了,垃圾手机系统的压力将会小很多。

同步消除

标量替换

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

除了它们自身的API库实现得好坏之外,其余的比较就成了一场“拼编译器”和“拼输出代码质量”的游戏。

Java虚拟机的即时编译器与C/C++的静态优化编译器相比,可能会由于下列这些原因导致输出的本地代码有一些劣势:

即时编译器运行占用的是用户程序的运行时间

Java语言是动态的类型安全语言

Java语言中虽然没有virtual关键字,但是使用虚方法的频率却远远大于C/C++语言

Java语言是可以动态扩展的语言

Java语言中对象的内存分配都是在堆上进行的,只有方法中的局部变量才能在堆上分配

5、本章小结
除了文章中有特别说明,均为IT宅原创文章,转载请以链接形式注明出处。
本文链接:http://www.itzhai.com/jvm-note-for-runtime-optimize.html
关键字: ,
arthinking Java技术交流群:280755654,入门群:428693174 more
分享到:
 
2015 3/1
如果您有更好的原创技术博文或者观点,欢迎投稿:admin@itzhai.com,或者关注订阅左侧浮动面板的微信号订阅IT宅itread)发送消息。
文章评论
    没有评论
给我留言

有人回复时邮件通知我
J2EE的相关文章
随机文章 本月热门 热评
1 【吉他曲子学习资源】Kotaro Oshio: wings you are the hero 2012/1/2
2 JavaScript设计模式笔记 – 适配器模式 装饰者模式 2012/11/19
3 Linux下find,sed,tr,grep,cut,wc等常用命令的使用和相关实例解析 2011/6/10
4 HTTP请求中的User-Agent 判断浏览器类型的各种方法 网络爬虫的请求标示 2013/8/12
5 UML笔记 UML统一建模语言介绍 UML图形的基本绘制 2011/10/9
6 Java Web笔记 – Tomcat部署项目 一个简单的Servlet编写 2011/11/8
友情推荐 更多
破博客 文官洗碗安天下,武将打怪定乾坤。多么美好的年代,思之令人泪落。
Mr.5's Life 白天是一名程序员,晚上就是个有抱负的探索者
行知-追寻技术之美 关注大数据,分布式系统
我爱编程 编程成长轨迹
Cynthia's Blog 学习笔记 知识总结 思考感悟
 
猜您喜欢
欢迎关注我的公众号 IT宅
关于IT宅 文章归档

IT宅中的文章除了标题注明转载或有特别说明的文章,均为IT宅的技术知识总结,学习笔记或随笔。如果喜欢,请使用文章下面提供的分享组件。转载请注明出处并加入文章的原链接。 感谢大家的支持。

联系我们:admin@itzhai.com

Theme by arthinking. Copyright © 2011-2015 IT宅.com 保留所有权利.