堆是分配对象的唯一选择吗?

一 逃逸分析

在《深入理解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"查看逃逸分析的筛选结果。

六 结论

开发中能使用局部变量的,就不要使用在方法外定义。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值