一 逃逸分析
在《深入理解Java虚拟机》中关于 Java 堆内存有这样一段描述:
随着 JIT 编译器的发展与逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化,所有的对象都分配到堆上也渐渐变得不那么“绝对”了。
在 Java 虚拟机中,对象是在 Java 堆中分配内存的,这是一个普遍的常识。但是,有一种特殊情况,那就是如果经过逃逸分析(Escape Analysis)后发现,一个对象并没有逃逸出方法的话,那么就可能被优化成栈上分配。这样就无需在堆上分配内存,也无须进行垃圾回收了。这也是最常见的堆外存储技术。
此外,基于 openJDk 深度定制的 TaoBaovm,其中创新的 GCIH(GC invisible heap)技术实现 off-heap,将生命周期较长的 Java 对象从 heap 中移至 heap 外,并且 GC 不能管理 GCIH 内部的 Java 对象,以此达到降低 GC 的回收频率和提升 GC 的回收效率的目的。
如何将堆上的对象分配到栈,需要使用逃逸分析手段。
这是一种可以有效减少 Java 程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法。
通过逃逸分析,Java Hotspot 编译器能够分析出一个新的对象的引用的使用范围,从而决定是否要将这个对象分配到堆上。
逃逸分析的基本行为就是分析对象动态作用域。
-
当一个对象在方法中被定义后,对象只在方法内部使用,则认为没有发生逃逸。
-
当一个对象在方法中被定义后,它被外部方法所引用,则认为发生逃逸。例如作为调用参数传递到其他地方中。
二 实战——没有发生逃逸
1 代码
public void my_method() {
V v = new V();
// use v
// ....
v = null;
}
2 说明
上面代码的 new V(); 是没有发生逃逸的对象,则可以分配到栈上,随着方法执行的结束,栈空间就被移除。
三 实战——发生了逃逸
1 代码
public static StringBuffer createStringBuffer(String s1, String s2) {
StringBuffer sb = new StringBuffer();
sb.append(s1);
sb.append(s2);
return sb;
}
2 说明
new StringBuffer() 发生了逃逸,return 后,该对象逃到方法外了。这个对象就不能使用栈上分配了。
如果想要 new StringBuffer() 对象不发生逃逸,可以改写为如下代码。
public static String createStringBuffer(String s1, String s2) {
StringBuffer sb = new StringBuffer();
sb.append(s1);
sb.append(s2);
return sb.toString();
}
此时 new StringBuffer() 对象不会逃逸出方法,它就能栈上分配了。
四 实战——完整的逃逸分析
/**
* 逃逸分析
* <p>
* 如何快速的判断是否发生了逃逸,就看 new 的对象实体是否有可能在方法外被调用。
*/
public class EscapeAnalysis {
public EscapeAnalysis obj;
/*
方法返回 EscapeAnalysis 对象,发生逃逸
*/
public EscapeAnalysis getInstance() {
return obj == null ? new EscapeAnalysis() : obj;
}
/*
为成员属性赋值,发生逃逸
如果当前的 obj 引用声明为 static,仍然会发生逃逸。
*/
public void setObj() {
this.obj = new EscapeAnalysis();
}
/*
对象的作用域仅在当前方法中有效,没有发生逃逸
*/
public void useEscapeAnalysis() {
EscapeAnalysis e = new EscapeAnalysis();
}
/*
引用成员变量的值,发生逃逸
*/
public void useEscapeAnalysis1() {
EscapeAnalysis e = getInstance();
// getInstance().xxx() 同样会发生逃逸
}
}
五 参数设置
在 JDK 6u23(JDK 7)版本之后,HotSpot 中默认就已经开启了逃逸分析。
如果使用的是较早的版本,开发人员则可以通过下面参数进行设置。
-
选项“-XX:+DoEscapeAnalysis"显式开启逃逸分析。
-
通过选项“-XX:+PrintEscapeAnalysis"查看逃逸分析的筛选结果。
六 结论
开发中能使用局部变量的,就不要使用在方法外定义。