逃逸分析的基本行为就是分析对象动态作用域:当一个对象在方法中被定义后,它可能被外部
方法所引用,例如作为调用参数传递到其他地方中,称为方法逃逸。
栈中分配对象(主要是经过逃逸分析之后,判断是否发生逃逸,没有逃逸的才可以在栈中分配) 在栈中分配对象,其实就是经过逃逸分析之后,如果一个对象没有发生逃逸,即在对象在子线
程中被分配,并且指向该对象的指针永远不会发生逃逸(即只有创建线程可以看见该对象),
那么经过逃逸分析之后,可以将对象分配在栈中,而不是分配在堆中,减少了垃圾回收
是否发生逃逸
JVM判断新创建的对象是否逃逸的依据有:
1、 对象被赋值给堆中对象的字段和类的静态变量。
2、 对象被传进了不确定的代码中去运行。
如果满足了以上情况的任意一种,那这个对象JVM就会判定为逃逸。
对于第一种情况,因为对象被放进堆中,则其它线程就可以对其进行访问,所以对象的使用情 况,编译器就无法再进行追踪。第二种情况相当于JVM在解析普通的字节码的时候,如果没有 发生JIT即时编译,编译器是不能事先完整知道这段代码会对对象做什么操作。保守一点,这 个时候也只能把对象是当作是逃逸来处理。
public static Object staticVariableObject;
public Object instanceObject;
public void staticVariableEscape(){
staticVariableObject = new Object();
//静态变量,外部线程可见,发生逃逸
}
public void instanceObjectEscape(){
instanceObject = new Object();
//赋值给堆中实例字段,外部线程可见,发生逃逸
}
public Object returnObjectEscape() {
return new Object();
//返回实例,外部线程可见,发生逃逸
}
public void noEscape(){
synchronized (new Object()){
//仅创建线程可见,对象无逃逸
}
Object noEscape = new Object();
//仅创建线程可见,对象无逃逸
}
}
逃逸分析优化
当判断出对象不发生逃逸时,编译器可以使用逃逸分析的结果作一些代码优化
1、 将堆分配转化为栈分配。如果某个对象在子程序中被分配,并且指向该对象的指针永远不 会逃逸,该对象就可以在分配在栈上,而不是在堆上。在有垃圾收集的语言中,这种优化可以 降低垃圾收集器运行的频率。栈上分配,就不需要垃圾回收。这里在栈中分配,对象需要比较 小
2、 同步消除。如果发现某个对象只能从一个线程可访问,那么在这个对象上的操作可以不需 要同步。
3、 分离对象或标量替换。如果某个对象的访问方式不要求该对象是一个连续的内存结构,那 么对象的部分(或全部)可以不存储在内存,而是存储在CPU寄存器中。
分析上面的例子:
JVM在逃逸分析之后,将对象分配在了方法noEscape()方法栈(栈帧中的操作数栈,存在局部 变量表)上。方法栈上的对象在方法执行完之后,栈桢弹出,对象就会自动回收。这样的话就 不需要等内存满时再触发内存回收。这样的好处是程序内存回收效率高,并且GC频率也会减 少,程序的性能就提高了。
在JVM中,几乎所有的对象都是在堆中分配的,但是也有例外,即对象被JVM判断永远不会发 生逃逸的,那么可以在栈上分配,在栈上分配,不需要进行垃圾回收,减少了垃圾回收的频率。
方法逃逸:
方法中创建的对象,只属于当前这个方法,没有传给其他的方法,就没有发生方法逃逸。
线程逃逸:
没有其他线程能够使用该对象
Was this helpful?
0 / 0