《java performance》读书笔记之JIT

          这一部分的内容其实主要是看了毕玄大师的《分布式java应用基础与实践》得到的总结。如下。

          编译是指从一些高级语言生成机器级别的指令的过程。传统的编译器一般是从C或者C++这样的源程序出发,把每个源代码文件编译成一个个对象文件,然后把这些文件连接到一个库或者可执行文件中,使得用户可以执行这些库或者可执行文件。因为编译并不是频繁的任务,所以编译时间在静态编译器上并不是大的约束。java使用了javac编译器,它读入上层的java源代码,然后把它转换成class文件,这些jar文件被组织成jar文件或者被加载进jvm。所以jvm总是以转换源程序的字节码开始,并动态得把字节码动态得转换成机器相关的指令。

          所有的编译器大体上都有着相似的结构。它要有一个从头到尾的源代码的表示从而把源代码转换成一个中间表示(IR)。编译器中有许多不同种类的IR,因为不同的IR会适用于编译的不同的阶段,所以一个编译器中实际上会在不 同的阶段中使用不同的IR。SSA(静态单赋值形式Static single assignment form)是一个经常会被用到的IR。它用来替换只被赋值一次的变量,且指令直接来使用这些值。这带来的一个好处是被指令使用的值可以直接被访问到。
          hostspot对解释执行的优化:1是采用了栈顶缓存,即将本来位于操作数栈顶的值直接缓存在寄存器上,这对于大部分只有一个操作数的操作而言,无须讲数据放入操作数栈,可直接在寄存器进行计算。 2是采用了部分栈帧共享的策略,因为通常传入另一方法的参数是当前方法已存放在操作数栈中的数据了。hotspot做了一个优化,即后一个方法在执行的时候可以使用前一个方法的操作数栈中的数据作为当前方法的局部变量。
          由于解释执行的效率较低,所以hotspot提供了将字节码编译为机器码的支持,编译在运行时进行,即just in time的编辑,称为jit编译器。jit编译器对在运行中执行频繁的代码进行编译,对不那么频繁的代码则继续采用解释的方式处理。在编译上,jit采用了两种形式,client 编译器和server编译器。
此外,jvm还做了其他方面的编译优化,比如方法内联、去虚拟化、冗余消除等。
          方法内联指的是在方法中调用了其他方法是,把被调用方法的字节码直接植入到当前方法中。可以在jvm启动参数中增加-XX:+PrintInLining来查看方法的内联信息。
去虚拟化是指在class文件加载以后,进行类层次分析,如果发现类中的方法只提供了一种实现,那么对于调用了这个方法的代码,也可以进行方法内联,从而提升执行的性能。
          冗余消除是指在编译时进行代码折叠或消除。比如代码中用了一个编译时常量static final boolean a = false;做为判断条件,例如:if(a){do something} ,那么这段代码就会被消除,因为它是永远也不会被执行到的代码。
          server 编译器又称为C2。它采用了大量的编译优化技巧进行优化,占用内存相对较多。C2会进行程序运行信息的收集,所以可以在全局范围上进行优化。逃逸分析是C2编译器很多优化的基础。逃逸分析是指在运行时判断变量是否被外部读取,如果会被读取就认为是逃逸的,基于逃逸分析C2可以做诸如标量替换、栈上分配、同步消除等优化。
           标量替换是指,当一个聚合量(即类对象)没有被用到只用到其中的标量是,编译器会把聚合量在编译时消除掉,只使用标量,不实例化聚合量。
 栈上分配是指对于一些没有逃逸的局部变量,因为不会被外部访问到,所以可以直接在栈上进行分配,而不是分配到堆上。栈上分配的好处是被分配的对象可以在方法调用结束时随着栈帧一起被处理掉,从而减少堆内存的分配,减轻对垃圾回收器的压力。
           同步消除是指在分析同步对象没有逃逸的情况下,把同步锁消除掉,这样可以省掉比较消耗性能的锁同步操作。但是我这里的问题是:变量没有逃逸的情况只能说明对象只在方法内部访问,没有对该对象同步访问的情况,但是并不表示使用了该对象锁内部的代码块也没有同步的必要了。
           触发jit的两个阈值:    CompileThreshold是指在方法被调用了多少次之后,就进行jit编译成字节码。在client模式下为1500次,在server模式下为10000次,这个值可以通过在启动时增加启动参数-XX:CompileThreshold=1000来设定。
           OnStackReplacePercentage用于设定是否触发OSR编译,client默认为933,server默认为140,这个值也可以通过参数-XX:OnStackReplacePercentage=140来设定。OSR编译是一种特殊的编译优化,它只替换方法循环体内的代码,因此在OSR编译之后出现的现象是整个方法体的代码被编译了,但是只有循环体的代码使用编译后的代码执行,方法内循环体外的代码依旧使用解释执行。

你可能感兴趣的:(java,优化,server,读书,performance,编译器)