JIT和AOT

JIT和AOT

JIT

JIT:just in time;动态将字节码编译成机器码,加快执行效率;

为什么不直接全部编译

真正程序运行,并不会执行全部的代码,大部分情况是20%的代码执行80%的请求;有些代码仅仅执行一次,就没必要编译;全部编译的代价大,内存占用高;

JIT通过热点检测,仅编译热点代码,可以权衡执行速度、内存占用、启动速度等方面

利用JIT预热

JIT的热点检测需要时间来识别出热点代码,再进行JIT编译优化;

可以利用这一特点,针对已知的热点请求,提前进行预热;让JIT感知热点代码,对其进行编译优化;

JIT预热只是底层的一个点,真正系统预热,涉及连接池初始化、线程启动等等;Sentinal的预热就是利用这些,当系统处于冷状态一段时间,大流量突然到来,应当缓慢放行;

逃逸分析

目的:通过算法,减少Java程序中内存分配到堆上;

判断是否逃逸:

  • 当一个对象在方法内定义,且只在方法内部使用,此时对象没有逃逸;

  • 当一个对象在方法内定义,又被外部方法引用,则认为发生逃逸,

JIT通过逃逸分析,优化代码:

1、当对象没有逃逸:JIT编译器就可能将其分配在栈上,而不是堆上;线程结束,栈内存回收,则回收此类对象;(栈内存速度更快,降低堆内存压力)

2、同步省略:当对象没有逃逸,只会被当前线程访问,那么在并发场景下,就会取消对此对象的同步,也叫锁消除;

举例:

// StringBuffer对象逃逸,分配到堆区
public static StringBuffer getStringBuffer(String s1, String s2) {
   StringBuffer sb = new StringBuffer();
   sb.append(s1);
   sb.append(s2);
   return sb;
}
// 不会发生逃逸,直接使用String的字面量,不分配内存到堆区;
public static String getString(String s1, String s2) {
   StringBuffer sb = new StringBuffer();
   sb.append(s1);
   sb.append(s2);
   return sb.toString();
}