介绍
逃逸分析是Java HotSpot Server编译器中JIT优化的一个重要步骤。它在Java SE 6u23及以后的版本中默认启用。
对象基于逃逸分析可以有三种状态:全局逃逸、参数逃逸和无逃逸。
- 全局逃逸:对象超出了方法或线程的范围,比如被存储在静态字段或作为方法的返回值。
public class A{
private static Object staticObject;
public void globalEscape(){
staticObject = new Object(); // 这个对象赋值给静态变量,因此是全局逃逸的
}
}
public static StringBuffer A(String s1,String s2){
StringBuffer sb = new StringBuffer();
sb.append(s1);
sb.append(s2);
return sb; // 也是全局逃逸的
}
- 参数逃逸:对象被作为参数传递或被参数引用,但在方法调用期间不会全局逃逸。
public class A{
public void methodA(){
Object localObject = new Object();
methodB(localObject);
}
public void methodB(Object param){
}
}
传递到methodB方法中的param对象就发生了参数逃逸。
- 无逃逸:对象可以被标量进行替换,意味着它的内存分配可以从生成的代码中移除。
public static String A(String s1,String s2){
StringBuffer sb = new StringBuffer();
sb.append(s1);
sb.append(s2);
return sb.toString();
}
不同的逃逸状态影响JIT(即时编译器)的优化策略:
- 全局逃逸: 由于对象可能被多个线程访问,全局逃逸的对象一般不适合进行栈上分配或其他内存优化。但JIT可能会进行其他类型的优化,如方法内联或循环优化。
- **参数逃逸:**这种情况下,对象虽然作为参数传递,但不会被方法外部的代码使用。JIT可以对这些对象进行一些优化,例如锁消除。
- 无逃逸::这是最适合优化的情况。JT可以采取多种优化措施,如在栈上分配内存,消除锁甚至完全消除对象分配(标量替换)。这些优化可以显著提高性能,减少垃圾收集的压力。
补充
什么是标量替换?
标量(Scalar)是指一个无法再分解成更小的数据的数据。Java中的原始数据类型就是标量。相对的,那些还可以分解的数据叫做聚合量(Aggregate),Java中的对象就是聚合量,因为他可以分解成其他聚合量和标量。
在JIT阶段,如果经过逃逸分析,发现一个对象不会被外界访问的话,那么经过JT优化,就会把这个对象拆解成若干个其中包含的若干个成员变量来代替。这个过程就是标量替换。
public static void main(String[] args){
alloc();
}
private static void alloc(){
Point point = new Point(1,2);
}
比如point对象没有逃逸出alloc方法,并且point对象是可以进行拆解成标量的,那么JIT就不会直接创建Point对象,而是直接用int x int y代替Point对象。