本文共 2088 字,大约阅读时间需要 6 分钟。
即时编译器(Just In Time Compiler,JIT编译器):Java程序最初是通过解释器进行解释执行的,当虚拟机发现某个方法或代码块的运行
特别频繁时,就会把这些代码认定为“热点代码”。为了提高热点代码的执行效率,在运行时,虚拟机将会把这些代码编译成与本地平台相关的机器码,并进行各种层次的优化,完成这个任务的编译器称为即时编译器。当程序需要迅速启动和执行的时候,解释器可以首先发挥作用,省去编译时间立即执行。
在程序运行后,随时间推移,编译器逐渐发挥作用,把越来越多的代码编译成本地代码之后,可以提供执行效率。HotSpot虚拟机中内置两个即时编译器,分别称为Client Compiler 和Server Compiler,简称C1编译器和C2编译器。
默认C1编译器,可以使用-client 或 -server 指定。解释器与编译器搭配使用的方式在虚拟机中称为混合模式,用户可以通过参数-Xint强制虚拟机运行与解释模式(Interpreted Mode),这时编译器完全不参与工作,全部代码都使用解释方式执行。也可以使用-Xcomp强制虚拟机运行编译模式(Compiled Mode),这时将优先采用编译方式执行程序。Client Compiler 可以获得更高的编译速度,Server Compiler可以获得更好的编译质量。热点代码:
被多次执行的循环体
判断一段代码是不是热点代码,是不是需要触发即时编译,这样的行为称为热点探测。热点探测方式:
方法调用计数器:统计方法被调用的次数,Client模式下默认1500次,Server模式下默认10000次,可以通过虚拟机参数-XX:CompiledThreshold修改。
回边计数器:统计一个方法中循环体代码执行次数。如果一个表达式E已经计算过了,并且从先前的计算到现在E中所有的变量的值都没有发生变化,那么E的这次出现就成为了公共子表达式。
对于这种表达式就没有必要花时间对它进行计算,只需直接用前面计算过的表达式结果代替E就可以了。int d = (c * b) * 12 + a + (a + b * c); int d = E * 12 + a + (a + E);
当这段代码进入虚拟机即时编译器编译后,就变为了第二行代码
java 在访问数组类型数据时会自动进行上下界范围检查,如果超出范围将抛出ArrayIndexOutOfBoundsException,每次数据读写都带有一次
隐含的条件判定,影响性能。对于循环体来说,如果编译器可以判定循环变量取值范围永远在[0,length())之间,那在整个循环中就可以把数组上下界检查消除。把目标的代码复制到发起调用的方法中,避免发生真实的方法调用。
分析对象动态作用域:当一个对象在方法中被定义后,它可能被外部方法所引用,例如作为调用参数传递到其他方法中,称为方法逃逸。甚至可能被外部线程访问到,譬如赋值给类变量或可以在其他线程中访问的实例变量,称为线程逃逸。 如果能证明一个对象不会逃逸到变量或线程外,也就是别的方法或线程无法通过任何途径访问到这个对象,则可能为为这个变量进行优化。如果确定一个对象不会逃逸出方法之外,那让这个对象在栈上分配,对象占用的内存就可以随栈帧出栈而销毁。
到的成员变量恢复原始类型来访问就叫标量替换。如果逃逸分析证明一个对象不会被外部访问,并且对象可以被拆散的话, 程序真正执行的时候
可能不创建这个对象,而改为直接创建它的若干被这个方法使用到的成员变量替换。这些变量建立在栈上分配读写。转载地址:http://cqnlx.baihongyu.com/