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

在《深入理解Java虚拟机》中关于Java堆内存有这样一段描述:

  随着JIT编译期的发展与逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化,所有的对象都分配到堆上也渐渐变得不那么“绝对”了。
  在Java虚拟机中,对象是在Java堆中分配内存的,这是一个普遍的常识。但是,有一种特殊情况,那就是如果经过逃逸分析(Escape Analysis)后发现,一个对象并没有逃逸出方法的话,那么就可能被优化成栈上分配。这样就无需在堆上分配内存,也无须进行垃圾回收了。这也是最常见的堆外存储技术。
  此外,前面提到的基于openJDK深度定制的TaoBaoVM,其中创新的GCIH (Gcinvisible heap)技术实现off-heap,将生命周期较长的Java对象从heap中移至heap外,并且Gc不能管理GCIH内部的Java对象,以此达到降低Gc的回收频率和提升GC的回收效率的目的。

逃逸分析: 判断是否逃逸依据看对象是否会被外部所调用

  将堆上的对象分配到栈,需要便用逃逸分析手段。
  这是一种可以有效减少Java程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法。
  通过逃逸分析,Java Hotspot编译器能够分析出一个新的对象的引用的使用范围从而决定是否要将这个对象分配到堆上。
  逃逸分析的基本行为就是分析对象动态作用域:
    当一个对象在方法中被定义后,对象只在方法内部使用,则认为没有发生逃逸。
    当一个对象在方法中被定义后,它被外部方法所引用,则认为发生逃逸。例如作为调用参数传递到其他地方中。

例1:

 对象A没有发生逃逸,直接在栈上分配,随着方法执行的结束,栈空间被移除;

public void myMethod(){
        A a =new A();
        ...
        a = null;
    }

 例2:

    //StringBuilder 逃逸
    public static StringBuilder getSb(String a ,String b){
        StringBuilder sb =new StringBuilder();
        sb.append(a);
        sb.append(b);
        return sb;
    }
    //StringBuilder 没有逃逸
    public static String getSbStr(String a ,String b){
        StringBuilder sb =new StringBuilder();
        sb.append(a);
        sb.append(b);
        return sb.toString();
    }

结论:

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

原文地址:https://www.cnblogs.com/zyf-yxm/p/13648653.html