逃逸分析
即时编译(Just-in-time Compilation,JIT)是一种通过在运行时将字节码翻译为机器码,从而改善性能的技术,在 HotSpot 实现中有多种选择:C1、C2 和 C1+C2,分别对应 Client、Server 和分层编译
- C1 编译速度快,优化方式比较保守;C2 编译速度慢,优化方式比较激进
- C1+C2 在开始阶段采用 C1 编译,当代码运行到一定热度之后采用 C2 重新编译
逃逸分析并不是直接的优化手段,而是一个代码分析方式,通过动态分析对象的作用域,为优化手段如栈上分配、标量替换和同步消除等提供依据,发生逃逸行为的情况有两种:方法逃逸和线程逃逸
- 方法逃逸:当一个对象在方法中定义之后,被外部方法引用
- 全局逃逸:一个对象的作用范围逃出了当前方法或者当前线程,比如对象是一个静态变量、全局变量赋值、已经发生逃逸的对象、作为当前方法的返回值
- 参数逃逸:一个对象被作为方法参数传递或者被参数引用
- 线程逃逸:如类变量或实例变量,可能被其它线程访问到
如果不存在逃逸行为,则可以对该对象进行如下优化:同步消除、标量替换和栈上分配
- 同步消除
线程同步本身比较耗时,如果确定一个对象不会逃逸出线程,不被其它线程访问到,那对象的读写就不会存在竞争,则可以消除对该对象的同步锁,通过-XX:+EliminateLocks
可以开启同步消除 ( - 号关闭) - 标量替换
- 标量替换:如果把一个对象拆散,将其成员变量恢复到基本类型来访问
- 标量 (scalar) :不可分割的量,如基本数据类型和 reference 类型
聚合量 (Aggregate):一个数据可以继续分解,对象一般是聚合量 - 如果逃逸分析发现一个对象不会被外部访问,并且该对象可以被拆散,那么经过优化之后,并不直接生成该对象,而是将该对象成员变量分解若干个被这个方法使用的成员变量所代替
- 参数设置:
-XX:+EliminateAllocations
:开启标量替换-XX:+PrintEliminateAllocations
:查看标量替换情况
- 栈上分配
JIT 编译器在编译期间根据逃逸分析的结果,如果一个对象没有逃逸出方法的话,就可能被优化成栈上分配。分配完成后,继续在调用栈内执行,最后线程结束,栈空间被回收,局部变量对象也被回收,这样就无需 GC