JVM——运行期优化

一、分层编译(TieredCompilation)

JVM 将执行状态分成了 5 个层次:

0层:解释执行,用解释器将字节码翻译为机器码
1层:使用 C1 即时编译器编译执行(不带 profiling)
2层:使用 C1 即时编译器编译执行(带基本的profiling)
3层:使用 C1 即时编译器编译执行(带完全的profiling)
4层:使用 C2 即时编译器编译执行

profiling 是指在运行过程中收集一些程序执行状态的数据,例如【方法的调用次数】,【循环的 回边次数】等

即时编译器(JIT)与解释器的区别

解释器
● 将字节码解释为机器码,下次即使遇到相同的字节码,仍会执行重复的解释
● 是将字节码解释为针对所有平台都通用的机器码

即时编译器
● 将一些字节码编译为机器码,并存入 Code Cache,下次遇到相同的代码,直接执行,无需再编译
● 根据平台类型,生成平台特定的机器码

对于大部分的不常用的代码,我们无需耗费时间将其编译成机器码,而是采取解释执行的方式运行;另一方面,对于仅占据小部分的热点代码,我们则可以将其编译成机器码,以达到理想的运行速度。 执行效率上简单比较一下 Interpreter(解释器) < C1 < C2,总的目标是发现热点代码(hotspot名称的由 来),并优化这些热点代码

逃逸分析

逃逸分析===>分析 new Object()对象是否会在循环外被用到或被其他方法所引用,发现其为循环内的局部变量即采用优化手段。
首先分析例子(每次创建1000个对象耗费所需的时间)
在这里插入图片描述

运行后发现大约在第70次循环时速度突然变快,大约在159次时速度一下降低到了三位数(在运行期间Java虚拟机会对代码做一定的优化
在这里插入图片描述
原因是什么呢?
—— JVM 将执行状态分成了 5 个层次

此种优化手段称之为逃逸分析,发现新建的对象是否逃逸。可以使用-XX:-DoEscapeAnalysis关闭逃逸分析

二、方法内联(Inlining)

内联函数
内联函数就是在程序编译时,编译器将程序中出现的内联函数的调用表达式用内联函数的函数体来直接进行替换

  private  static int square(final int i){
        return i*i;
    }

如果发现square使热点方法,并且长度不太长时,会进行内联,所谓的内联就是把方法内代码拷贝、粘贴到调用者位置:

System.out.printf(9*9);

还能够进行常量折叠的优化
System.out.printf(81);

例子
在这里插入图片描述
大约在153次后花费时间为0,进行内联后将其看作常量
在这里插入图片描述

三、反射优化

public class Reflect1 {
   public static void foo() {
      System.out.println("foo...");
   }

   public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
      Method foo = Demo3.class.getMethod("foo");
      for(int i = 0; i<=16; i++) {
      // 执行反射调用(静态方法,无关联实例对象,因此参数为null)
         foo.invoke(null);  
      }
   }
}

结果:前16次调用性能相对较低,第17次调用开始性能会大大提升

foo.invoke 前面 0 ~ 15 次调用使用的是 MethodAccessor 的 NativeMethodAccessorImpl 实现

invoke方法源码

@CallerSensitive
public Object invoke(Object obj, Object... args)
    throws IllegalAccessException, IllegalArgumentException,
       InvocationTargetException
{
    if (!override) {
        if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
            Class<?> caller = Reflection.getCallerClass();
            checkAccess(caller, clazz, obj, modifiers);
        }
    }
    //MethodAccessor是一个接口,有3个实现类,其中有一个是抽象类
    MethodAccessor ma = methodAccessor;             // read volatile
    if (ma == null) {
        ma = acquireMethodAccessor();
    }
    return ma.invoke(obj, args);
}

在这里插入图片描述
会由DelegatingMehodAccessorImpl去调用NativeMethodAccessorImpl

NativeMethodAccessorImpl源码

class NativeMethodAccessorImpl extends MethodAccessorImpl {
    private final Method method;
    private DelegatingMethodAccessorImpl parent;
    private int numInvocations;

    NativeMethodAccessorImpl(Method var1) {
        this.method = var1;
    }
	
	//每次进行反射调用,会让numInvocation与ReflectionFactory.inflationThreshold的值(15)进行比较,并使使得numInvocation的值加一
	//如果numInvocation>ReflectionFactory.inflationThreshold,则会调用本地方法invoke0方法
    public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
        if (++this.numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(this.method.getDeclaringClass())) {
            MethodAccessorImpl var3 = (MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod(this.method.getDeclaringClass(), this.method.getName(), this.method.getParameterTypes(), this.method.getReturnType(), this.method.getExceptionTypes(), this.method.getModifiers());
            this.parent.setDelegate(var3);
        }

        return invoke0(this.method, var1, var2);
    }

    void setParent(DelegatingMethodAccessorImpl var1) {
        this.parent = var1;
    }

    private static native Object invoke0(Method var0, Object var1, Object[] var2);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

new一个对象_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值