这节我们总结一下JVM运行期的优化问题。

JVM运行期优化

  Java程序在运行的期间,可能会有某个方法或者代码块的运行特别频繁时,就会把这些代码认定为“热点代码”。为了提高热点代码的执行效率,在运行时JVM会将这些代码编译成与本地平台相关的机器码,并进行各种层次的优化,完成这个任务的编译器称为即时编译器(Just In Time Compiler,JIT编译器)。

即时编译器(JIT)

  事实上,现在许多主流的商用虚拟机,都同时包含有解释器与编译器,解释器与编译器两者各有优势。与解释器相比,编译器会将常运行到的代码编译成本地代码区实现,可以获取更高的执行效率。而当程序运行环境中内存资源限制较大时,可以使用解释执行节约内存,反之可以使用编译执行来提高效率。解释器和编译器之间还可以通过逆优化退回到解释状态继续执行,因此,在整个虚拟机执行架构中,解释器与编译器经常配合工作。 
  HotSpot编译器中出现两个编译器Client Compiler称为C1编译器,Server Compiler称为C2编译器。JVM默认采用解释器与其中一个编译器直接配合的方法工作,程序使用哪个编译器,取决于虚拟机运行的模式,HotSpot虚拟机会根据自身版本与宿主机器的硬件性能自动选择运行模式,用户也可以使用“-client”或“server”参数去强制指定虚拟机运行在Client模式还是Server模式。 
  解释器与编译器搭配使用的方式在虚拟机中称为“混合模式”,用户可以使用参数”-Xint”强制虚拟机运行于解释模式,也可以使用“-Xcomp”强制虚拟机运行于编译模式。 
  为了在程序启动响应速度与运行效率之间达到最佳平衡,HotSpot虚拟机还会逐渐启动分层编译的策略,分层编译的概念在JDK1.6中出现,后来一直处于改进阶段,最终在JDK1.7的Server模式虚拟机中作为umoren编译策略被开启。分层编译根据编译器编译、优化的规模与耗时,划分出不同的编译层次,其中包括:

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

编译对象与触发条件

编译对象

在运行过程中会被即时编译器编译的“热点代码”有两类: 
1、被多次调用的方法; 
2、被多次执行的循环体。 
对于第一种情况,由于是由方法调用触发的编译,因此编译器会以整个方法作为编译对象,这种编译也是虚拟机中标准的JIT编译方式。而对第二种情况,尽管编译动作是由循环体所触发的,但编译器依然会以整个方法作为编译对象,这种编译方式因为编译发生在方法执行过程之中,因此形象地被称为栈上替换,简称为OSR编译,即方法栈帧还在栈上,方法就被替换了。

触发条件

  在这里提一个问题,被多次调用,在这里的多次具体是多少次?并且虚拟机如何统计一个方法或一段代码被执行过多少次? 
判断一段代码是不是为热点代码,是不是需要触发即时编译,这样的行为称为热点探测,但进行热点探测也是不一定要知道方法具体被调用了多少次,目前主要的热点探  测判定方法有两种:

  • 基于采样的热点探测:虚拟机会周期性地检查各种线程的栈顶,如果发现某个或者某些方法经常出现在栈顶,那这个方法就是“热点方法”。 
    优点:实现简单、高效,还可以很容易地获取方法调用方法。 
    缺点:很难精确地确认一个方法的热度,容易因为受到线程阻塞或别的外界因素的影响而扰乱热点探测。
  • 基于计数器的热点探测:虚拟机会为每个方法尽力计数器,统计方法的执行次数,如果执行次数超过一定的阙值就认为它是热点方法。 
    优点:统计结构相对来说更加精确与严谨。 
    缺点:实现起来麻烦,需要为每个方法及建立并维护计数器,而且不能直接获取到方法的调用关系。 
    在HotSpot中使用的是第二种方法,基于计数器的热点探测法,因此它为每个方法准备了两类计数器:方法调用计数器和汇编计数器(统计一个方法中循环体代码执行的次数,在字节码中中遇到控制流向后跳转的指令称为“回边”。建立回边计数器的目的是为了触发OSR编译。)。

编译过程:

在默认设置下,无论是方法调用产生即使编译请求,还是OSR编译请求,虚拟机在代码编译器还未完成之前,都仍然将按照解释方式继续执行,而编译动作则在后台的编译线程中进行。 
在后台执行编译的过程中,编译器做了什么?Server Compiler和Client Compiler两个编译器的编译过程是不同的,对于Client Compiler来说,它是一个简单快速的三段式编译器,主要的关注点在于局部性的优化,而 放弃了许多耗时较长的全局优化手段。 
  1、一个平台独立前段将字节码构造成一种高级中间代码表示(HIR),HIR使用 静态单分配的形式来表示代码值,这可以使得一些在JIR的构造过程之中和之后进行的优化动作更容易实现。在此之前,编译器会在字节码上完成一部分基础优化,如方法内联、常量传播等。 
  2、一个平台相关的后端从HIR中产生低级中间代码表示,而在此之前,在HIR上完成另外一些优化, 如空值检查消除、范围检查消除等。 
  3、在平台的后端使用线性扫描算法在LIR上分配寄存器,并在LIR上做窥孔优化,然后产生机器代码。

  对于Server Compiler则是专门面向服务端的典型应用并为服务端的性能配置特别调整过的编译期,也是一个充分优化过的高级编译器,它会执行所有经典的优化动作。 
Server Compiler的寄存器分配器是一个全局图着色分配器,它可以充分利用某些处理器架构上的大寄存器集合。所以它也是比较缓慢的,但是编译代码质量高,可以减少本地代码的执行时间,从而抵消了额外的编译时间开销,所以很多非服务端的应用选择使用server模式的虚拟机运行。

编译优化技术

虚拟机中的具有代表性的优化技术: 
语言无关的经典优化技术之一:公共子表达式消除。 
语言相关的经典优化技术之一:数组范围检查消除。 
最重要的优化技术之一:方法内联 
最前沿的优化技术之一:逃逸分析

    • 公共子表达式消除 
      如果一个表达式E已经计算过了,并且从先前的计算到现在E中所有变量的值都没有发生变化,那E的这次出现就成公共子表达式,可以用原先的表达式进行消除。

    • 数组边界检查消除 
      系统将自动进行上下界的范围检查。 
      隐式异常处理:Java中空指针和算术运算中除数为零的检查。此外还有:自动装箱消除、安全点消除、消除反射等等。

    • 方法内联 
      把目标方法的代码“复制”到发起调用的方法之中,避免发生真实的方法调用。

    • 逃逸分析 
      分析对象的动态作用域,当一个对象在方法中被定义后,它可能被外部方法所引用,例如作为调用参数传递给其他方法,称为方法逃逸。甚至还有可能被外部线程访问到,比如赋值给类变量或可以在其他线程中访问到的实例变量,称为线程逃逸。 
      如果能证明一个对象不会逃逸到方法或线程之外,也就是别的方法或线程无法通过任何途径访问到这个对象,就可以为这个变量进行一些高效的优化:如:栈上分配、同步消除、标量替换等。

JVM总结(六):晚期(运行期)优化的更多相关文章

  1. JVM原理:4 运行期优化

    JVM运行期优化 Java程序在运行的期间,可能会有某个方法或者代码块的运行特别频繁时,就会把这些代码认定为“热点代码”.为了提高热点代码的执行效率,在运行时JVM会将这些代码编译成与本地平台相关的机 ...

  2. jvm虚拟机笔记<六> 运行期优化

    这节我们总结一下JVM运行期的优化问题. https://www.cnblogs.com/zhouyuqin/p/5224573.html JVM运行期优化 即时编译器(JIT) 编译对象与触发条件 ...

  3. 深入了解JVM虚拟机8:Java的编译期优化与运行期优化

    java编译期优化 java语言的编译期其实是一段不确定的操作过程,因为它可以分为三类编译过程:1.前端编译:把.java文件转变为.class文件2.后端编译:把字节码转变为机器码3.静态提前编译: ...

  4. JVM(1)---虚拟机在运行期的优化策略

    1.解释器与JIT编译器 首先我们先来了解一下运行在虚拟机之上的解释器与JIT编译器.当我们的虚拟机在运行一个java程序的时候,它可以采用两种方式来运行这个java程序: 采用解释器的形式,也就是说 ...

  5. JIT晚期(运行期)

    在部分的商用虚拟机(Sun HotSpot.IBM J9)中,Java程序最初是通过解释器(Interpreter)进行解释执行的,当虚拟机发现某个方法或代码块的运行特别频繁时,就会把这些代码认定为& ...

  6. 【深入理解JAVA虚拟机】第4部分.程序编译与代码优化.2.运行期优化。这章提到的具体的优化技术,应该对以后做性能工作会有帮助。

    1.概述 Java程序最初是通过解释器(Interpreter)进行解释执行的,当虚拟机发现某个方法或代码块的运行特别频繁时,就会把这些代码认定为“热点代码”(Hot Spot Code). 为了提高 ...

  7. JVM 第六篇:极致优化 IDEA 启动速度

    本文内容过于硬核,建议有 Java 相关经验人士阅读. 1. 引言 相信做 Java 开发的同学,对 IDEA 这个工具应该都不陌生,即使不使用 IDEA 做开发,那么对 Eclipse 这个工具应该 ...

  8. Java虚拟机11:运行期优化

    前言 http://www.cnblogs.com/xrq730/p/4839245.html,HotSpot采用的是解释器+编译器并存的架构,之前的这篇文章里面已经讲过了,本文只是把即时编译器这块再 ...

  9. Java虚拟机15:运行期优化

    前言 HotSpot采用的是解释器+编译器并存的架构,之前的这篇文章里面已经讲过了,本文只是把即时编译器这块再讲得具体一点而已.当然,其实本文的内容也没多大意义,90%都是概念上的东西,对于实际开发. ...

  10. 运行期优化 Java内存模型与线程 线程安全与优化

随机推荐

  1. PHP 闭包获取外部变量和global关键字声明变量的区别

    最近在学习workerman的时候比较频繁的接触到回调函数,使用中经常会因为worker的使用方式不同,会用这两种不同的方式去调用外部的worker变量,这里就整理一下PHP闭包获取外部变量和glob ...

  2. shell脚本--内容查找之grep命令

    grep命令可以检索文件中包含关键字(可以使用正则)的行,默认区分大小写. ubuntu@ubuntu:~/test$ cat test.txt this is linux this is Linux ...

  3. PHP常用工具类积累

    第一 请求第三方接口的工具类 例如,封装了get和post请求方法的工具类,代码如下: <?php class HttpClient{ /** * HttpClient * @param arr ...

  4. 理解 Delphi 的类(十) - 深入方法[18] - 在接口区声明的方法都相当于提前声明了

    //要点18: 如果函数在接口区定义了, 就无需用 forward 提前声明了 unit Unit1; interface uses   Windows, Messages, SysUtils, Va ...

  5. jdk1.8 HashMap的扩容resize()方法详解

    /** * Initializes or doubles table size. If null, allocates in * accord with initial capacity target ...

  6. CSS等高布局的7种方式

    前面的话 等高布局是指子元素在父元素中高度相等的布局方式.等高布局的实现包括伪等高和真等高,伪等高只是看上去等高而已,真等高是实实在在的等高.本文将介绍边框模拟.负margin这两种伪等高以及tabl ...

  7. List泛型集合

    List和数组 相同点: 都可以控制元素类型 不同点: List的长度是可变的,所以list比数组更容易掌控 List属性 1.Count 获取集合中实际包含的元素个数 2.Capcity 集合中可以 ...

  8. Mysterious Bacteria LightOJ - 1220

    题意: 给出一个数x 求 x = bp  的p的最大值 解析: 算术基本定理 分解质因数 任何一个数x都可以表示为  x == p1a1   *  p2a2  *  ````` * pnan 即  b ...

  9. 自定义缓存设计(static缓存数据)

    设计题 编程过程中,为了解决缓存数据共享的问题,常常会使用static关键字达到脱离具体实例化对象,在整个java进程生命周期内共享数据的目的.请编写一个类,类名为MapCache,拥有但不局限于以下 ...

  10. 今天一天课,随便写点吧/xk

    知道的越多,不知道的也就越多. 最近想学很多很多东西,但是发现没有一个计划,也没有那么多时间精力,都是想到哪学到哪,有的就是学了一半就放下了,又去学新的,感觉需要规划一下学习路线,时间什么的 推荐一个 ...