一、逃逸分析
概念:一种确定指针动态范围的静态分析,它可以分析在程序的哪些地方可以访问到指针
Java 虚拟机中的逃逸分析针对的是新建对象
即时编译器判断对象是否逃逸的依据:
1.对象是否被存入堆中(静态字段或者堆中对象的实例字段)
2.对象是否被传入未知代码中
public void forEach(ArrayList<Object> list, Consumer<Object> f) {
Itr iter = new Itr; // 注意这里是new指令
iter.cursor = 0;
iter.lastRet = -1;
iter.expectedModCount = list.modCount;
while (iter.cursor < list.size) {
if (list.modCount != iter.expectedModCount)
throw new ConcurrentModificationException();
int i = iter.cursor;
if (i >= list.size)
throw new NoSuchElementException();
Object[] elementData = list.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
iter.cursor = i + 1;
iter.lastRet = i;
Object obj = elementData[i];
f.accept(obj);
}
}
新建的ArrayList$Itr实例既没有被存入任何字段之中,也没有作为任何方法调用的调用者或者参数。因此,逃逸分析将断定该实例不逃逸。
二、基于逃逸分析的优化
即时编译器可以根据逃逸分析的结果进行诸如锁消除、栈上分配以及标量替换的优化。
锁消除
锁对象不逃逸就意味着该锁对象的加、解锁都没有意义。
即时编译器可以消除对该不逃逸锁对象的加锁、解锁操作
synchronized (new Object()) {}会被完全优化掉。这正是因为基于逃逸分析的锁消除。由于其他线程不能获得该锁对象,因此也无法基于该锁对象构造两个线程之间的 happens-before 规则
栈上分配
(1)若不使用逃逸分析技术,对象创建方式:
JVM对象都是在堆上分配的,堆上的内容对任何线程可见,JVM需要对所分配的堆内存进行管理。
需要借助垃圾回收器来处理不再被引用的对象。
(2)若使用逃逸分析技术,对象创建方式:
逃逸分析证明新建对象不可逃逸之后:
若证明某些新建的对象不逃逸,JVM可以将其分配至栈上,并且在 new 语句所在的方法退出时,通过弹出当前方法的栈桢来自动回收所分配的内存空间。
无须借助垃圾回收器来处理不再被引用的对象。
标量替换
标量:仅能存储一个值的变量,比如 Java 代码中的局部变量
聚合量:可能同时存储多个值,其中一个典型的例子便是 Java 对象
标量替换这项优化技术,可以看成将原本对对象的字段的访问,替换为一个个局部变量的访问。
对上述代码标量替换后,由于Itr对象没有被实际分配,因此和栈上分配一样,它同样可以减轻垃圾回收的压力。
public void forEach(ArrayList<Object> list, Consumer<Object> f) {
// Itr iter = new Itr; // 经过标量替换后该分配无意义,可以被优化掉
int cursor = 0; // 标量替换
int lastRet = -1; // 标量替换
int expectedModCount = list.modCount; // 标量替换
while (cursor < list.size) {
if (list.modCount != expectedModCount)
throw new ConcurrentModificationException();
int i = cursor;
if (i >= list.size)
throw new NoSuchElementException();
Object[] elementData = list.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
lastRet = i;
Object obj = elementData[i];
f.accept(obj);
}
}
部分逃逸分析
public static void bar(boolean cond) {
Object foo = new Object();
if (cond) {
foo.hashCode();
}
}
// 经过部分逃逸分析,优化为:
public static void bar(boolean cond) {
if (cond) {
Object foo = new Object();
foo.hashCode();
}
}