一、概念
逃逸分析:指的是对象是否会逃逸出当前方法,进入堆内存中或者作为参数传入被未知方法引用。如果在堆中那么该对象会被所有线程看见并引用,未知方法包括虚方法,对象传入虚方法中就算逃逸。逃逸分析可以根据逃逸范围实现以下多个编译优化的好处,缺点是分析本身需要消耗一定的CPU资源;
逃逸:因为当对象被多个线程引用或者被虚方法引用时,编译器无法定位对象的位置,所以被认为是逃逸了;
逃逸范围:(范围由小到大)
- 不逃逸:只在一个方法中被引用(支持同步消除、标量替换、栈上分配优化)
- 方法逃逸:在同一个线程内的多个方法中被作为参数传递(不支持标量替换优化)
- 线程逃逸:在多个线程中被引用(不支持栈上分配优化)
二、基于逃逸分析的编译优化
优化一:
同步消除:当对象不会逃逸出当前线程时,在对象上加的锁会被虚拟机优化去除,因为该对象不会被其他线程引用,是线程安全的(比如在单线程中的对象上加的锁)
优化二:
标量替换:指的是不再将对象存储在堆中,而是用一个个单独的局部变量组合成对象具体的内容,并且将这些局部变量分开存储在栈或者寄存器中。这样就可以减轻GC的压力了,而且还可以节省内存空间。
对象标量替换的条件:不会发生逃逸的对象,也就是不需要放入到堆中供所有线程引用的对象 — 这里指的是方法中new的对象,不发生方法逃逸的对象
优化三:
栈上分配:理论上可以将这类不逃逸的对象放入到栈帧中(不在堆中分配内存),也就是栈上分配,但是实现过程复杂,HotSpot没有将其实现,所以用标量替换代替了 — 这里指的是不发生线程逃逸的对象(线程中包含多个方法,每个方法内部可能会new对象)。
优化四:
部分逃逸分析:指的是对象不一定会发生逃逸,在某些情况下当满足判断条件进入另一块程序时对象才会发生逃逸(比如if语句)。
为了减少对象创建,即时编译器会将对象在堆中建立的时间推延至该对象发生逃逸的时间点,如果该对象在极大概率下不会发生逃逸,那么就可以通过上述标量替换的方法达到优化的目的。
参考文章
https://time.geekbang.org/column/article/18048