JVM内存分配

我们知道在jvm内存模型中,分为堆、虚拟机栈、方法区、本地方法栈、程序计数器,其中堆又分为新生代和老年代,那么对象的在堆是如何分配呢?

1.新生代内存分配

          新生代分为eden space,from space和to space三个区域,from space和to space这两个去又叫Survivor区,它们之间的比例为(8:1:1),这个比例也是可以修改的。

2.内存分配

          现在是尝试分配三个2MB的对象和一个4MB的对象,然后我们通过JVM参数 -Xms20M、-Xmx20M、-Xmn10M 把Java堆大小设置为20MB,不可扩展。其中10M分配给新生代,另外10M分配给老生代。然后我们通过-XX:SurvivorRatio=8来分配新生代各区的比例,设置为8,表示eden与一个survivor区的空间比例为8:1。

          注意:新生代空间10M(其中eden 8M,两个Survivor(各1M)),老年代空间10M,前三个对象一共6M,最后一个对象是4M

          正常流程把前三个对象放入了新生代Eden区,当第四个对象放入的时候,发现新生代Eden区已经使用6M,剩余2M,但第四个对象为4M,很明显放Eden区不进去,Survivor区各位1M也放不进去,那么怎么办呢?此时会发生一次Minor GC(稍后介绍),将6M的对象全部移除新生代,放入老年代(10M),再讲4M的对象放入新生代Eden区,此时内存分配为:Eden区4M,老年代6M,内存分配完成。

          那么问题来了,如果老年代也满了怎么办?

          空间分配担保

          其实在在发生Minor GC之前,虚拟机会检测之前每次晋升到老年代的的平均大小是否大于老年代的剩余空间大小。

         1>、若满足,则改为直接进行一次Full GC(稍后介绍)。

         2>、若不满足,则查看HandlePromotionFailure设置是否允许担保失败。若参数为true,允许担保失败,则只会进行Minor GC。若参数为false,则改为进行一次Full GC。

        也就是说,当在发生Minor GC之前,先看一下老年大空间够不够,不够的话,发生Full GC;够的话,放入;如果发生Full GC后,没有可回收的,那么发生OutofMemory异常。

3.大对象直接进入老年代

        什么事大对象?大对象就是指需要大量连续内存的Java对象,最典型的大对象就是那种很长的字符串及数组

        虚拟机提供了一个-XX:PretenureSizeThreshold参数,令大于这个设置值的对象直接在老年代中分配。如果一个对象是4M,-XX:PretenureSizeThreshold是3M,那么此对象直接放入老年代。

4.长期存活的对象进入老年代

        什么是长期存活呢?在这之前需要了解什么是对象年龄计数器。

   对象年龄计数器

       如果对象在Eden出生并经过第一次Minor GC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor空间中,并将对象年龄设为1。对象在Survivor区中每熬过一次Minor GC,年龄就增加1岁,当它的年龄增加到一定程度(默认15岁)时,就会晋升

到老年代中。对象晋升老年代的阈值,可以通过参数-XX:MaxTenuringThreshold来设置。

       那么超过这个值的对象都视为长期存活对象。

5.动态对象年龄判定

          为了能更好的适应不同程序的内存情况,虚拟机不一定要求对象年龄到达MaxTenuringThreshold才能晋升老年代。如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或者等于该年龄的对象直接进入老年代,无需等到

MaxTenuringThreshold要求的年龄。

         例如:Survivor大小为10M,那么一半就是5M,其中存放了6个年龄是2,大小为1M的对象,共6M,(Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半),那么年龄大于等于2的对象进入老年代。

6.新生代GC 和 老年代GC

        新生代通常存活时间较短基于复制算法进行回收,所谓复制算法就是扫描出存活的对象,并复制到一块新的完全未使用的空间中,对应于新生代,就是在Eden和FromSpace或ToSpace之间copy。新生代采用空闲指针的方式来控制GC触发,指针保持到最后一个分配的对象在新生代区间的位置,当有新的对象要分配内存时,用于检查空间是否足够,不够就触发GC。当连续分配对象时,对象会逐渐从Eden到Survivor,最后到老年代。

        老年代与新生代不同,老年代对象存活的时间比较长、比较稳定,因此采用标记(Mark)算法来进行回收,所谓标记就是扫描出存活的对象,然后再进行回收未被标记的对象,回收后对用空出的空间要么进行合并、要么标记出来便于下次进行分配,总之目的就是要减少内存碎片带来的效率损耗。

具体算法介绍,请直接找度娘或者查看书籍资料

参考资料《深入理解Java虚拟机》

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值