重识JVM(六):内存分配原则

对象的内存分配,就是在堆上分配,对象主要分配在新生代的Eden 区上,如果启动了本地线程分配缓冲,将按线程优先在TLAB 上分配少数情况下也可能直接分配在老年代中,分配的规则不是固定的,其细节取决于当前使用的是哪一种垃圾收集器组合,还有虚拟机中与内存相关的参数设置。
下面会讲解几条最普遍的内存分配原则。
对象优先在Eden 分配
大多数情况下,对象在新生代Eden 区中分配。当Eden 区没有足够空间进行分配时,虚拟机将发起一次Minor GC。如果GC 期间虚拟机发现已有的对象全部无法放入Survivor 空间,会通过分配担保机制提前转移至老年代中。
大对象直接进入老年代
所谓的大对象是指,需要大量连续内存空间的Java 对象,最典型的大对象就是那种很长的字符串以及数组。经常出现大对象容易导致内存还有不少空间就提前触发垃圾收集以获取足够的连续空间来安置它们。
长期存活的对象将进入老年代
虚拟机为每个对象定义一个对象年龄(Age)计数器。如果对象在Eden 出生并经过第一次Minor GC 后仍然存活,并且能够被Survivor 容纳的话,将被移动到Survivor 空间中,并且对象年龄设为1.对象在Survivor 区中每熬过一次Minor GC,年龄就增加1 岁,当它的年龄增加到一定程度(默认为15 岁),就将会被晋升到老年代。
动态对象年龄判定
虚拟机并不是永远地要求对象的年龄必须达到MaxTenuringThreshold 才能晋升老年代,如果在Survivor 空间中相同年龄所有对象大小的总和大于Survivor 空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代。
空间分配担保
在发生Minor GC 之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这个条件成立,那么Minor GC 可以确保是安全的。如果不成立,则虚拟机会查看HandlePromotionFailure 设置值是否允许担保失败。如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次Minor GC,尽管这次Minor GC 是有风险的;如果小于,或者HandlePromotionFailure设置不允许冒险,那此时也要改为进行一次Full GC。
冒险是指当出现大量对象在Minor GC 后仍然存活的情况,就需要老年代进行分配担保,把Survivor 区无法容纳的对象直接进入老年代。老年代要进行这样的担保,前提是老年代本身还有容纳这些对象的剩余空间,一共会有多少对象会活下来在实际完成内存回收之前是无法明确知道的,所以只好取之间每一次回收晋升到老年代对象容量的平均大小值作为经验值,与老年代的剩余空间进行比较,决定是否进行Full GC 来让老年代腾出更多空间。取平均值进行比较其实仍然是一种动态概率的手段,依然存在担保失败的情况。如果出现了HandlePromotionFailure 失败,那就只好在失败后重新发起一次Full GC。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值