为何Java程序会执行一段时间后跑的更快?



对于Java 应用,程序员之间一个认识口口相传: 
java

要看一个Java程序跑的快不快,须要多跑几回;另外,Java程序跑一段时间以后会快起来。速度甚至能遇上 C/C++程序的速度。


若是你问为何跑一段时间就快了呢?git

通常都能听到 「由于JVM会把调用次数多的热方法编译再执行」的答案。程序员


更通俗的话来说, JVM 会把热方法编译成机器码,执行效率会更高。就像公司或工厂里,对于一项任务,通常老手都比新人更快,由于老手更熟悉嘛。因此招聘要求里你不多会见到指明要新人的,大部分都是要有工做经验的。github


而JVM 将热方法编译生成的机器码,因为是针对当前平台,当前硬件生成的,对应用具体执行状况分析以后进行编译而成,因此就像老手同样,能更了解状况,效率固然更高。web


默默在背后作编译工做的人就是 JIT (Just-In-Time) 编译器,通常也叫即时编译器。
数据库



今天咱们一块儿来看看,这越跑越快的背后,JIT 具体是怎样工做的。tomcat


咱们都知道,Java 原生就是解释型语言,也是解释执行的,怎么又有了编译执行了?微信

执行 java -version 的时候,咱们通常能看到当前 Java 版本号以后,会有一个 mixed mode,说明当前JVM 运行在混合模式之下,即同时包含解释执行和编译执行。咱们也能够经过参数强制执行只按一种模式执行。各类环境根据本身的须要选择执行的方式。app


相比编译执行,解释执行要慢不少,但仍然普遍在被运用在各类虚拟机中,好比它内存占用少,应用启动时间更短。更关键的优点在于它简单。一种新语言或者一个语言的新特性出现时,在解释器中能比编译器实现要快不少。另外,开发者会考虑到性价比,一些语言特性很难,同时也不值得在实如今编译器就只使用解释器。工具


开发实现语言时,使用解释器只有两个要求:

  1. 熟悉VM实现语言

  2. 理解新语言特性、语法和语义


而像在JIT编译器实现新语言特性,对开发者有更多的要求:

  • 熟悉目标机器的应用程序二进制接口规范

  • 把新语言特性映射到这个目标机器的接口运行时

  • 掌握开发编译器生成目标机器码的能力



而为了应用程序的执行效率、运行速度, Java 又特别须要JIT,在运行的适当时候,能够把一些高频率代码编译,换取更好的效率。


JIT就是经过将热方法、代码段编译生成机器码的形式,在下次调用到该方法时,会直接经过vtable中连接的机器码直接执行,因此效率是杠杠的。


那么问题来了,什么样的方法才算热方法,怎样来判断热方法?


对于热方法的计算,通常虚拟机内有如下几种实现方式:

  • 基于方法的JIT,JVM内经常使用

  • 基于踪影的JIT, Dalvik和 TraceMonkey在使用

  • 基于区域的JIT,HHVM 使用这种形式


基于方法的JIT中,通常探测热点方法有基于采样的热点探测,即周期性的去检查线程的调用栈顶,若是方法常常出如今栈顶,那它就是热点方法。另外一种是基于计数器的热点探测,这种会给每一个方法创建计数器,用来统计方法的执行次数。超过阈值的就认为是热点方法。


固然须要注意的是,这里统计的次数,不是绝对的次数,和咱们进行限流和降级时说的相似,都是一个时间周期内的相对频率,若是在此期间没有超过,就不算,原来的次数会减小。


JIT 编译的代码,存储在 Code Cache 的内存区间。空间是有限的在JVM 启动的时候,设置了一个固定的最大值,实现形式也是个堆,在分配满时会中止编译,类卸载、替换成新版本等也会从 Code Cache中删除。

另外,在JVM JIT编译器中包含C一、C2    两种编译器,在具体的编译过程当中,通常是采用分层编译,再具体使用不一样的编译器,相比C1,C2编译须要更多的时间,作更多的优化等等,像内联、循环展开、逃逸分析、锁消除与合并、栈上替换……

前面咱们大概了解了JIT的原理,也了解到 JIT 编译后,机器码执行效率更高,那有什么办法能了解到咱们本身的应用里,JIT有没有执行,用的是C1仍是C2,对哪些代码作过编译和优化呢?


咱们有没有办法,能知道都有哪些方法被JIT编译了,哪些方法原本咱们想要效率高一些,期待被编译却没被考虑的,能更直观的知道呢?

一个办法是应用启动时,增长 JVM 参数:

-XX:+UnlockDiagnosticVMOptions

-XX:+PrintCompilation

-XX:+PrintInlining

-XX:+PrintCodeCache

-XX:+PrintCodeCacheOnCompilation

-XX:+TraceClassLoading

-XX:+LogCompilation

-XX:LogFile=~/a.log


而后根据这些输出内容,以及日志文件里的内容,去分析。

固然,若是真的是肉眼阅读那可太累了。好在有一个优秀的开源工具用于解析日志文件。

铛铛铛,来了。

就是它, JITWatch。

https://github.com/AdoptOpenJDK/jitwatch

使用 JavaFX 开发而成,功能很强大。



你能够 经过 Open Log 直接解析上面输出的日志文件。  例如一个简单的应用,打开日志以后,会看到不一样包下的内容,这里example111 是示例。

    public void jitTest() { long x = calc();        System.out.println(x); }
public long calc() { long sum = 0; for (long i=0; i< 1000000; i++) { sum = plus(sum, i); } return sum; }
public long plus(long a, long b) { return a + b; }




在点击右侧某个JIT编译过的具体方法后,点击TriView,会看到生成的节字码,以及相应的源码是如何对应到字节码和汇编代码的。



点击Chain,会看到编译链路


Inline-info 会显示哪些方法进行了内联优化。

这里看到的OSR,就是常听到的栈上替换(On-stack replacement),用于优化在解释器中执行时,向后跳转的循环分支达到某个阈值时就会被编译。



JITWatch 还有一个沙箱的环境,能够用来实验观察 JIT的行为,观察 JVM 里JIT的决策过程。


有了工具的帮助,咱们能更好的理解JIT 对应用优化的决策,从而让应用性能更佳。



若有帮助,帮忙在看转发支持我一把吧,感谢。


近期文章

五分钟爆改,把你的JSON/CSV文件打形成MySQL数据库

JVM:有些内部信息我悄悄告诉你

不用Jar 包的Agent?几行代码实现运行时加强?

理解了 1+2的过程,就理解了Java虚拟机

快放开那些捣乱的猴子!

Java七武器系列孔雀翎-- 问题诊断神器BTrace

Java七武器系列长生剑 -- Java虚拟机的显微镜 Serviceability Agent

写代码效率不高?放过Ctrl C 和 V,让AI来能帮你写代码吧

怎样阅读源代码?




这里是「Tomcat那些事儿」,关注源码|实战|成长等话题,欢迎关注,一块儿成长。


本文分享自微信公众号 - Tomcat那些事儿(tomcat0000)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索