JVM垃圾回收(四)- 内存分配与回收策略

相关概念

先引入两个概念,在JVM堆内存中,又分为新生代和老年代

  • 新生代:主要是用来存放新生的对象。一般占据堆的1/3空间
  • 老年代:主要存放应用程序中生命周期长的内存对象

内存分配规则

对象优先在Eden分配

大多数情况下,对象优先在新生代Eden区中分配。当Eden区没有足够的空间进行分配的时候,将发一起一次MminorGC

大对象直接进入老年代

所谓大对象就是需要大量连续空间的Java对象,最典型的大对象就是那种很长的字符串及数组。虚拟机提供了一个-XX:PretenureSizeThreshold参数。
令大于这个设置值的对象直接在老年代分配。目的是为了避免在Eden区及两个survivor区之间发生大量的内存复制。

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

虚拟机对每个对象定义了一个Age计数器。如果对象在Eden出生后经过第一次Miror GC,年龄就增加一岁。当它的年龄增加到一定的程度(默认15岁)就会晋升到老年代中。
可以通过参数-XX:MaxTenuringThreshold进行设置。

动态年龄的判定

除了年龄达到了MaxTenuringThreshold的对象晋升如老年代。如果在Survivor空间相同年龄所有对象大小总和大于Survivor空间的一半。年龄大于或等于该值的对象就可以直接进入老年代。

空间分配担保

  JDK6之前,在发生Minor GC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间
  如果检查老年代最大可用的连续空间大于新生代所有对象总空间,那么Minor GC可以确保是安全的
  如果检查不成立,则虚拟机会查看HandlePromotionFailure设置值是否允许担保失败
  如果允许担保失败,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,这里取平均值是因为在实际完成内存回收前无法明确知道有多少对象会存活下来,所以也存在一定风险
  如果大于历次平均大小,将尝试着进行一次Minor GC,尽管这次Minor GC是有风险的,Minor GC若执行失败也会进行执行一次Full GC
  如果小于历次平均大小,或者HandlePromotionFailure设置不允许冒险,那这时也要改为进行一次Full GC
  对以上的步骤归纳一下,先看老年代的可用空间能否容下新生代的所有对象,不能的话看是否开启了分配担保机制,允许就先执行Minor GC,否则直接进行Full GC。大部分情况下还是会将HandlePromotionFailure开启分配担保,避免频繁Full GC
  JDK6后,HandlePromotionFailure不再影响到虚拟机的空间分配担保策略,变为只要老年代的连续空间大于新生代对象总大小或历次晋升的平均大小就会进行Minor GC,否则将进行Full GC。

posted @ 2020-05-05 20:30  遗失的岁月  阅读(109)  评论(0编辑  收藏  举报