JAVA性能优化权威指南 读书笔记四

时间:2020-12-09 05:57:50

HotSpot JIT编译器

简介

  编译指的是从编程完成的代码转化到机器能够是使用的机器码的过程,在这个过程中开发人员可能需要等待很长一段时间才能得到最终可以运行的软件。JAVA采用了另外一种角度来解决这个问题,由于JAVA是基于JVM虚拟机的一种语言,这就使得java可以将编译的过程分为两个步骤。先将代码编译成虚拟机JVM能够看懂的部分(class),然后再由虚拟机进行动态的将应用执行的class代码动态的转换为机器码并最终执行。

  由于这个编译分开的步骤,所以java才真正意义上拥有了一次编译到处运行的优势和特点,因为详细的本地机器码翻译被下放到各个不同环境的JVM中去了,在代码以及初次编译的时候开发人员就不用考虑这个问题。

 

类继承关系分析

  由于在java中类的关系比较复杂,特别是涉及到方法覆盖和重载,优化的过程就会变得比较麻烦,所以在jvm中使用了CHA类型继承关系分析,其基础的思想是只考虑已经加载的子类,并将信息记录在编译代码中,如果执行的过程中有后续的代码覆盖其中的方法时,之前的优化会被丢弃,JVM使用逆优化来解决这个问题。

 

编译策略

  之前有论述将java的编译过程分为两个部分,但是JVM那个部分是在运行的时候才会发生,毕竟JVMjava程序需要运行的时候才会用到的东西,以下的情况也就顺理成章了:JIT没有时间编译所有方法,所有代码最初都是在解释器中运行。

  但如果全部都是用解释执行,那么效率将会成为一个很大的问题,所以在JVM中采取了这样的机制:一旦方法被调用的次数比较多,那么这个方法就能得到编译。

  具体的执行通过两个方法的计数器方法调用计数器以及回边计数器来完成,方法调用计数器每次调用方法值加一,回边计数器每次代码从后跳转至前加一(循环等步骤)。超过JVM设置的阀值那么这个方法就会得到编译。

  一般的过程是这样的,解释器通知JIT编译器执行编译,但是另一方面解释器继续执行代码,直到编译完成之后编译代码会自动关联方法,当然这个部分也可以通过改变JVM中的参数值进行设置。

  当长时间的java循环的时候JVM会使用栈上替换的方法,这个操作的关键就是不直接编译方法,而是编译解释器帧,通过直接编译解释器帧的代码使得在循环过程中也能够充分利用编译的成果。

 

逆优化

    JVM中还对于编译以及运行有更高的容错机制,那就是逆优化。就是将编译帧转换为解释器的解释帧,这个能将编译过程中的某些乐观优化退回,同时这个过程允许了在编译过程中出现的更大更激进的乐观优化。并且由于在HotSpot中采用方法活跃性的分析法,将逆优化的内存成本极大的降低。

 

Client JIT编译器以及Server JIT编译器

      Client JIT编译器的目标是更快的启动以及快速编译,早期的Client JIT编译器就是一个简单快捷的代码生成器,在1.4开始支持非那个发内联,到了1.6有了比较大的改动,基本就是性能提高构架改动等。

      Server JIT编译器目标是在应用的稳定运行的时间范围内,将应用的性能推向极致,编译的时候优化幅度大,但是这同样带来了编译成本的提高。

Server JIT编译器的中间代码IR,是一个基于SSAIR,使用不同的方式展现控制流--程序依赖图。这种方式企图捕获每次执行过程中的最小约束,将其反馈给优化器做出比较大幅度的优化计算。

  所有基于JAVA字节码的JIT编译器都需要处理卸载或未初始化的类,Server采用的方式是停止解析并生成罕见陷阱,请求逆优化,然后在下一次编译触发的时候重新编译。

      Server JIT对于代码的循环做了大量的优化,循环判断外堤、循环展开、迭代分离。迭代分离将循环转换成3个部分:预循环、主循环、后循环,将主循环所需要的边界判断消除,并直接展开主循环。超字,循环向量化的方式,如循环在内存操作上是线性的,就可以合成一个矢量操作。