JRockit读书笔记I — Java代码的高效执行

Posted on

JRockit读书笔记I — Java代码的高效执行

BlueDavy之技术blog

{互联网,OSGi,Java, High Scalability, High Performance,HA}

JRockit读书笔记I — Java代码的高效执行

Dec 16

bluedavyjvm java code generation, java代码执行, jit, jvm 10 Comments 《Oracle JRockit: The Definitive Guide》一书是由Oracle JRockit的两位资深开发人员写的,其中的Marcus Hirt更是JRockit Mission Control的leader,这本书详细的对Oracle JRockit进行了介绍,最突出的特点非常系统化的介绍了一个JVM通常是如何实现的,而JRockit这样一个极为优秀的JVM又是做了哪些优化,为什么做这些优化,这本书对于对JVM感兴趣的同学而言应该是必读的一本书,其实即使对于JVM兴趣不强的同学,里面的优化思路的介绍也是值得学习,本系列的blog主要是总结看这本书得到的一些收获,由于书中知识量巨大,因此得分成多篇blog来总结了。

书的第二章为:Adaptive Code Generation,在这章中作者向我们讲解了一个优秀的JVM是如何来实现代码的高效执行的,感兴趣的同学其实可以在不看下面blog内容之前,先考虑下如果是你做的话,你会怎么做来实现Java代码的高效执行呢,然后再对比下这章的内容,我想你能学到很多的,:)

用过Java的同学都知道,Java是通过javac将Java源码编译为class文件,然后通过ClassLoader装载此class文件,之后就可执行此class了,要最高效的执行这个class,最好的方法莫过于class文件直接就是机器码,这样直接执行就可以了,但Java是跨平台的,因此class文件就不能是机器码了。

由于class文件不是直接的机器码,要执行它最简单的方法就是采用纯粹的解释方式,解释方式由于每次都得将class文件中的指令翻译为对应的机器环境的指令,效率是很低的。

为了能更高效的执行,同时又保持跨平台的特性,另外一个方法就是在执行class时再将其翻译为对应的机器码,这个方法是比较靠谱的,因此无论是Hotspot、还是JRockit,都采用了这种方式,也就是大家熟知的JIT(Just In Time) Compiler。

OK,既然觉得在装载class后翻译成机器码去执行可以比较高效,那这个时候又会出现两种状况,是执行class的时候就立刻翻译成机器码,还是先用解释模式执行,然后到一定时机再翻译成机器码呢,之所以出现这两种状况,原因在于将class翻译为机器码是需要消耗时间的,因此如果执行class的时候就立刻翻译成机器码的话,也就会导致Java程序启动速度会比较慢,JRockit是这么认为的,JRockit的服务对象是server级应用,这类应用的特点是没那么在乎启动速度,而更在乎的是执行时的高效,而且如果执行的时候就立刻翻译成机器码的话,就意味着压根不需要实现解释器,因此JRockit采取的方法是在执行class时直接编译为机器码,而Hotspot由于需要同时支持client和server应用,对于client应用而言,启动速度非常重要,因此Hotspot采用的是先解释执行,到了一定时机后再翻译成机器码。

如果认为就这样就完成了Java代码的执行的实现,那就太小看JVM了,由于JVM能够知道代码运行的全部状况,自然还可以做出更多更出色的提升代码执行速度的优化,例如标量替换、更好的inline等,后面再来细说,因此这样就出现了一个状况,什么时候对哪些代码来做这些更猛的优化呢。

真正值得做更猛的优化的代码自然是所谓的”热点”代码,如何来发现哪些代码是热点代码呢,通常有三种方法: 1、方法调用计数器 方法调用计数器是常见的方式,hotspot采用的即为这种,这种方式不好的地方就在于计数器本身经常是cpu cache misses的,因此稍微会有点影响性能。 2、对线程进行采样 可采用软件或硬件方式来实现,软件方式实现不好的地方在于采样的时候需要暂停线程,好处是因为是采样,不需要对所有方法进行计数,硬件方式自然是最好的,但不是所有的硬件都支持的,支持的硬件中最典型的是intel IA-64的CPU。

在有了发现热点代码的方法后,接下来需要做的就是更猛的优化,有很多种,例如Java的代码中,通常会是接口方式的调用,但因为是接口方式的调用,所以其实默认情况下是不好做inline处理的,但JVM为了更高效的执行代码,如发现这代码为热点代码,那么就会做一些激进的优化,例如会假设这个接口只有一个实现,然后就可以直接将此实现对应的代码inline进来了(至于为什么inline后效率更高,这个请参考编译原理之类的书),这些激进优化同样适合于if、抛异常这些状况,当然,当激进优化的条件失效时,就会逆优化回到之前基本编译的代码。 而其他的更猛的优化还包括根据线程执行路径进行逃逸分析等,后面再专门写一篇blog来讲解下一些翻译为机器码的优化吧,其实大多都是编译原理的一些东西。

书中在介绍JRockit如何实现自己的JIT Compiler时,提到了Bytecode混淆以及bytecode优化,JRockit的态度是bytecode混淆时将name进行混淆是靠谱的,但如果对control flow进行混淆,就不太好了,因为这有可能会导致jit compile时的有些优化也做不了了,而bytecode优化,JRockit的态度是应该避免,因为没什么太大的意义,更主要的优化还是得靠jit compiler。

JRockit的JIT Compiler的实现和Hotspot另外一个很大的不同在于JRockit并未采用on-stack replacement,据JRockit的研究,这个没有太大必要,当然,对于编写benchmark代码时则要注意这个不同。

JIT Compiler在compile时还需要考虑的几个重点问题: 1、为GC提供必要的信息; 2、为查错提供必要的信息,例如代码的行数、变量名等;

从这章的内容可以看到,JRockit为了能够让Java代码能够高效的执行,是做出了非常多的努力的,也可以看到很多JRockit与Hotspot不同的地方,甚至可以看出Java代码的执行比C代码的执行高效都是有可能的,:)。

学习JVM的References Sun JDK 1.6内存管理

10 Comments (+add yours?)

  1. h Dec 16, 2010 @ 18:57:35 我还没有读完……啊……
  2. cauherk Dec 16, 2010 @ 20:44:19 JRockit好像不是在执行的时候进行jit 编译操作的吧。 打开jrmc的flight record,可以详细的看到jit编译的情况,其中并没有列出所有执行过的代码。
  3. jianying Dec 16, 2010 @ 23:09:24 运行时的内联优化的确是可能比C更快的。
  4. bluedavy Dec 17, 2010 @ 09:49:57 @cauherk JRockit是没有解释器的,如果不编译,你觉得它该怎么执行呢,具体可以看看书中的这章。 “When the method is first called, and control jumps to the trampoline, all it does is execute a call that tells JRockit that the real method needs to be generated.”
  5. bluedavy Dec 17, 2010 @ 09:51:28 @cauherk flight recorder多数情况是采样机制和阀值机制,因此没包含所有执行过的代码也是正常的。
  6. hanguokai Dec 17, 2010 @ 13:46:45 对线程进行采样,采了什么,如何判断热点?
  7. bluedavy Dec 17, 2010 @ 21:39:38 @hanguokai 对线程进行采样,看看线程在执行些什么方法,采样多次后,自然也就知道哪里是热点了。
  8. Wen Jan 11, 2011 @ 22:33:45 您好!我是一名在校大学生,看了您写的这篇博文,很感兴趣。我现在正在做一个关于JVM优化的问题,刚刚开始,用C写了一个简单的Bytecode解释器,能够进行类型转换,算术逻辑运算,控制转移等工作,我和我的同学希望能够做出一个面向移动嵌入式平台的小型JVM,现在遇到一些问题,能请您给我们一些建议吗? 我们的问题主要集中在优化的方向选择上,现在大致有两条思路:一个是将bytecode反编译为一种中间语言(比如C),再通过中间语言编译机器码实现其功能,另一个是优化bytecode编译的方式。这是大体思路,我们对JVM的理解还很粗浅,见笑了~ 谢谢!
  9. RednaxelaFX Jan 14, 2011 @ 15:36:25 @Wen 这两种思路都有现成的实现,如果想参考的话资源非常丰富。 例如说,同样是应用在嵌入式领域的小型JVM,Squawk(http://labs.oracle.com/projects/dashboard.php?id=155 https://squawk.dev.java.net/)就是先将Java字节码编译为C,然后再用C编译器编译为native code,再部署到设备上的。 如果想在运行时直接将字节码编译为native code,那么嵌入式领域可以参考phoneME(https://phoneme.dev.java.net/)开源的CDC HotSpot Implementation或者CLDC HotSpot Implementation。或者像是Google Android里的Dalvik虚拟机,里面也有JIT编译器,并且使用的是最近比较流行的trace-based compiler。

对这些感兴趣的话,也欢迎来JavaEye的高级语言虚拟机圈子来讨论 ^_^ http://hllvm.group.javaeye.com/

Leave a Reply

Cancel

Name (required)

Mail (required)

Website

CAPTCHA Image

Refresh Image

CAPTCHA Code /*


July 2013 M T W T F S S « Mar 1234567 891011121314 15161718192021 22232425262728 293031

Categories

Tags

btrace c1 c2 Deflater facebook gc gc tuning Grizzly HBase hotspot Inflater interpreter javac java code generation JavaOne javaone general technical session java代码执行 Java 并发 jit jvm memory management Native Memory Leak NoSQL oom oracle keynote pessimism policy references RPC serial gc SOA sun jdk sun jdk oom Web容量规划的艺术 yuanzhuo 书:分布式Java应用 书评 互联网技术 交流 内存管理 分布式Java应用 圆桌交流 容量规划 悲观策略 服务框架 硅谷公司

订阅

feedsky 抓虾 google reader 鲜果 九点

推荐书籍

My Book

© BlueDavy之技术blog 2013

Icons & Wordpress Theme by N.Design

希望本站内容对您有点用处,有什么疑问或建议请在后面留言评论
转载请注明作者(RobinChia)和出处 It so life ,请勿用于任何商业用途