JVM之堆

堆在JVM规范里是一种通用性的内存池(也存在于RAM中),用于存放所有的Java对象。堆是一个运行时数据区,类的对象从中分配空间。这些对象通过New关键字被建立,它们不需要程序代码来显式地释放。堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存大小,生存周期也不需要事先告诉编译器。由于它是在运行时动态分配内存的,Java的垃圾收集器会自动收走那些不再使用的数据。但缺点是,由于要在运行时动态分配内存,所以数据存取速度较慢。大多数的虚拟机里,Java中的对象和数组都存放在堆中。


堆不同于栈的好处是,编译器不需要知道要从堆里分配多少存储区域,也不必知道存储的数据在堆里需要存活多长时间。因此,在堆里分配存储相较于栈来说,有很大的灵活性。当你需要创建一个对象的时候,只需要引用New关键字写一行简单的代码,当执行这行代码时,会自动在堆里进行存储分配。当然,为这种灵活性必须要付出相应的代价,即用堆进行存储分配比用堆栈进行存储存储需要更多的时间。


Java堆区在JVM启动的时候即被创建,它只要求逻辑上是连续的,在物理空间上可以是不连续。所有的线程共享Java堆,在这里可以划分线程私有的缓冲区(Thread Local Allocation Buffer,TLAB)。


如前所述,Java堆区是一块用于存储对象实例的内存区,同时也是GC(GarbageCollection,垃圾收集器)执行垃圾回收的重点区域。正是因为Java堆区是GC的重点回收区域,那么GC极有可能会在大内存的使用和频繁进行垃圾回收过程上成为系统性能瓶颈。为了解决这个问题,JVM的设计者们开始考虑是否一定需要将对象实例存储到Java堆区内。基于OpenJDK[1]深度定制的TaobaoJVM[2],其中创新的GCIH(GC invisible heap)技术实现了off-heap,即将生命周期较长的Java对象从heap中移到heap之外,并且GC不能管理GCIH内部的Java对象,以此达到降低GC的回收频率和提升GC的回收效率的目的。除此之外,逃逸分析与栈上分配这样的优化技术同样也是降低GC回收频率和提升GC回收效率的有效方式。这样一来,Java堆区就不再是Java对象内存分配的唯一选择了。目前主流的垃圾收集算法是按代收集,即按照对象的生存时间分为新生代和老年代。新生代又进一步被划分为Eden区、FromSurvivor区和To Survivor区,主要是为了垃圾回收用途。这里浅显地提一些垃圾回收知识,详细内容请参考第7章节。


逃逸分析英文全名是Escape Analysis。在计算机语言编译器优化原理中,逃逸分析是指分析指针动态范围的方法,它同编译器优化原理的指针分析和外形分析相关联。计算机软件方面,逃逸分析指的是计算机语言编译器语言优化管理中,分析指针动态范围的方法。通俗点讲,如果一个对象的指针被多个方法或线程引用时,那我们可以称这个指针发生了逃逸。Java语言也有逃逸情况存在,示例代码如清单2-6所示。


代码清单2-6 逃逸分析示例代码 escapeAnalysisClass

public classescapeAnalysisClass{ 

    public static B b; 

 

    public voidglobalVariablePointerEscape(){//给全局变量赋值,发生逃逸

        b=new B(); 

    } 

 

    public B methodPointerEscape(){//方法返回值,发生逃逸

        return new B(); 

    } 

 

    public voidinstancePassPointerEscape(){ 

       methodPointerEscape().printClassName(this);//实例引用发生逃逸

    } 

 

 

 

class B{ 

    public void printClassName(G g){ 

       System.out.println(g.getClass().getName()); 

    } 

public class G {

     public static B b;

     public voidglobalVariablePointerEscape(){//给全局变量赋值,发生逃逸

         b=new B();

     }

     public B methodPointerEscape(){//方法返回值,发生逃逸

         return new B();

     }

     public void instancePassPointerEscape(){

        methodPointerEscape().printClassName(this);//实例引用发生逃逸

     }

 }

 class B{

     public void printClassName(G g){

        System.out.println(g.getClass().getName());

     }

 }

代码清单2-6所示的这个例子中,一共举了3种常见的指针逃逸场景,分别是全局变量赋值、方法返回值、实例引用传递。



[1]    OpenJDK做为GPL许可(GPL-licensed)的Java平台的开源化实现,Sun正式发布它已经六年有余。从发布那一时刻起,Java社区的大众们就又开始努力学习,以适应这个新的开源代码基础(code-base)。

[2]    由AliJVM团队发布,是AliJVM团队基于OpenJDK HotSpot VM发布的国内第一个优化、定制且开源的服务器版Java虚拟机。目前已经在淘宝、天猫上线,全部替换了Oracle官方JVM版本。


欢迎关注麦克叔叔每晚10点说,让我们一起交流与学习。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值