什么是逃逸分析
逃逸分析(Escape Analysis)是一种Java虚拟机的优化技术,它的主要目的是识别出那些不会逃逸出方法作用域的对象,从而可以对这些对象进行优化,提高程序的性能和效率。
逃逸分析的主要目的是什么?
逃逸分析的主要目的是识别出那些不会逃逸出方法作用域的对象,从而可以对这些对象进行优化。如果一个对象不会逃逸出方法作用域,那么它可以被分配在栈上,而不是堆上。由于栈的分配和回收速度比堆要快,因此可以提高程序的性能和效率。逃逸分析的主要目的是为了做优化。
满足逃逸分析需要的条件如下
1、对象的创建只在方法内部进行,并且不会被方法外部引用。
这个条件意味着,如果对象只在方法内部使用,并且不会被返回或者传递给其他方法,那么它就不会逃逸出方法的作用域。这样的对象可以被分配在栈上,而不是堆上,从而提高程序的性能和效率。
2、对象不会被多线程共享,也不会被外部线程访问。
如果对象不会被多个线程共享,并且不会被其他线程访问,那么它就不会逃逸出当前线程的作用域。这样的对象也可以被分配在栈上,而不是堆上。
3、对象不会被反射机制调用。
如果对象不会被反射机制调用,那么它就不会逃逸出当前方法的作用域。因为反射机制可以绕过Java语言的静态检查,从而获取对象的引用并进行访问,所以如果对象会被反射机制调用,那么它就无法进行逃逸分析。
Java满足逃逸分析需要的条件比较严格,只有满足了所有的条件才能进行逃逸分析。但是如果能够进行逃逸分析,就可以对程序进行优化,提高程序的性能和效率。
变量的作用域
局部变量,不会发生逃逸
全局变量,会发生逃逸
逃逸分析如何优化Java程序的性能和效率
- 锁消除 (如果局部变量加了锁,则会直接消除锁)
- 栈上分配(判断程序中的对象是否存在逃逸情况,如果不存在,则可以将对象分配在栈上而不是堆上,从而避免在堆上进行内存分配和回收的开销)
- 标量替换
1. 标量:不可再分,基本数据类型
2. 聚合量:可在分,引用类型
3. 对不可再分的直接进行填充
标量替换
int x=1,y=2;
System.out.println(x);
System.out.println(y);
替换
System.out.println(1);
System.out.println(2);
逃逸分析
使用JVM参数来开启逃逸分析:
注意: 从Java 6开始,逃逸分析就默认是开启的。但是,有些情况下,JVM会禁用逃逸分析,例如:
- 在使用了一些特定的JVM参数时,例如
-XX:+PrintCompilation
、-XX:+PrintInlining
等。 - 在使用了一些特定的JavaAPI时,例如
System.identityHashCode()
、java.lang.ref.WeakReference
等。 - 在使用了一些特定的编译器插件或者工具时。
- 在使用了一些特定的代码模式或者设计模式时,例如单例模式等。
- 在这些情况下,逃逸分析可能会被禁用,从而无法对程序进行优化。如果需要确保逃逸分析一直处于开启状态,可以使用JVM参数
-XX:+DoEscapeAnalysis
来显式开启逃逸分析。
java -XX:+DoEscapeAnalysis EscapeAnalysisDemo
示例代码
这个示例代码创建了100000000个Point对象,并且获取每个对象的x属性。由于这些对象的创建和使用都在方法内部进行,并且不会被方法外部引用、不会被多线程共享、不会被反射机制调用,所以它们都可以被分配在栈上,而不是堆上。这样就可以避免在堆上进行内存分配和回收的开销,提高程序的性能和效率。
public class EscapeAnalysisDemo {
public static void main(String[] args) {
long start = System.currentTimeMillis();
for (int i = 0; i < 100000000; i++) {
Point p = new Point(i, i);
p.getX();
}
long end = System.currentTimeMillis();
System.out.println("Time: " + (end - start) + "ms");
}
private static class Point {
private int x;
private int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
}