GC回收调优中的一些概念

一、JVM中对象进入老年代的4种情况
1、大对象直接进入老年代
大对象就是需要大量连续内存空间的对象(比如:字符串、数组)。JVM参数 -XX:PretenureSizeThreshold 可以设置大对象的大小,如果对象超过设置大小会直接进入老年代,不会进入年轻代,这个参数只在 Serial 和ParNew两个收集器下有效。
比如设置JVM参数:-XX:PretenureSizeThreshold=1000000 (单位是字节) -XX:+UseSerialGC ,再执行下上面的第一个程序会发现大对象直接进了老年代

为什么要这样呢?
为了避免为大对象分配内存时的复制操作而降低效率。

2、长期存活的对象将进入老年代
既然虚拟机采用了分代收集的思想来管理内存,那么内存回收时就必须能识别哪些对象应放在新生代,哪些对象应放在老年代中。为了做到这一点,虚拟机给每个对象一个对象年龄(Age)计数器。
如果对象在 Eden 出生并经过第一次 Minor GC 后仍然能够存活,并且能被 Survivor 容纳的话,将被移动到 Survivor 空间中,并将对象年龄设为1。对象在 Survivor 中每熬过一次 MinorGC,年龄就增加1岁,当它的年龄增加到一定程度(默认为15岁,CMS收集器默认6岁,不同的垃圾收集器会略微有点不同),就会被晋升到老年代中。对象晋升到老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold 来设置。

3、对象动态年龄判断
HotSpot虚拟机并不一定会严格按照设置的年龄阈值,满足以下条件也能直接进入老年代:Survivor 区中,年龄从 1 到 n 的对象大小之和超过 Survivor 区的 50% 时,新生代中年龄大于等于 n 的对象将进入老年代。

例如Survivor区域里现在有一批对象,年龄1+年龄2+年龄n的多个年龄对象总和超过了Survivor区域的50%,此时就会把年龄n(含)以上的对象都放入老年代。这个规则其实是希望那些可能是长期存活的对象,尽早进入老年代。对象动态年龄判断机制一般是在minor gc之后触发的。

4、老年代空间分配担保机制
在上一个规则中,我们知道新生代Survivor存放的空间不足时,会转移到老年代里,如果老年代空间也不足了呢?

这就引出了我们现在要说的这个规则,老年代空间分配担保机制。
jvm在Minor Gc之前,会检查老年代可用空间,是否大于新生代所有对象的大小,如果空间满足,那么自然不用说,按照前面的规则开始执行。
如果空间不满足,这个时候如果出现一种极端情形,也就是年轻代的对象在gc之后,全部都还存活,那么老年代的空间也不够存放了,这时候要怎么办呢?

这个时候要看一个参数 -XX:HandlePromotionFailure 是否设置了,如果设置了就会进行新的检查:现在老年代的空间是否满足于之前每次Minor Gc后进入老年代对象的平均大小,如果满足了,说明这次Minor Gc有可能也是空间足够的,所以,会直接进行Minor Gc;如果不满足或者 -XX:HandlePromotionFailure 参数未配置,那么会直接进行Full Gc,如图所示:

![在这里插入图片描述](https://img-blog.csdnimg.cn/5d901dc7760848af80027d7999b4c2af.png

在进行空间分配担保之后Minor Gc,会出现几种可能:

Minor Gc之后,存活对象小于Survivor 区域,那么就会直接进入到Survivor区域。
Minor Gc之后, 存活对象大于Survivor 区域,小于老年代空间,直接进入到老年代空间。
Minor Gc之后,存活对象还是大于老年代空间,那么还是会进行Full Gc,如果Full Gc后,空间还是不够,则抛出 OutMemoryException异常。

二、GC回收
1、Minor GC/Young GC:指发生新生代的的垃圾收集动作,Minor GC非常频繁,回收速度一般也比较快。
2、Major GC/Full GC:一般会回收老年代 ,年轻代,方法区的垃圾,Major GC的速度一般会比Minor GC的慢10倍以上。

Eden与Survivor区默认8:1:1
大量的对象被分配在eden区,eden区满了后会触发minor gc,可能会有99%以上的对象成为垃圾被回收掉,剩余存活的对象会被挪到为空的那块survivor区,下一次eden区满了后又会触发minor gc,把eden区和survivor区垃圾对象回收,把剩余存活的对象一次性挪动到另外一块为空的survivor区,因为新生代的对象都是朝生夕死的,存活时间很短,所以JVM默认的8:1:1的比例是很合适的,让eden区尽量的大,survivor区够用即可,

3、G1的混合回收MixedGC

(1)什么是Mixed GC
当越来越多的对象晋升到老年代Old Region时,为了避免内存被耗尽,虚拟机会触发一个混合的垃圾收集器,即Mixed GC,该算法并不是一个Old GC,除了回收整个Young Region,还会回收一部分的Old Region。
Mixed GC也是STW的,在用户指定的停顿时间目标范围内完成GC回收。

(2)Mixed GC触发条件
1)当达到IHOP阈值,-XX:InitiatingHeapOccupancyPercent(默认45%)时,即,当使用内存占到堆总大小的45%的时候,G1将开始并发标记阶段 + Mixed GC。
2)当全局并发标记结束后,统计出所有可能被回收的垃圾占Heap的比例值,如果超过5%(-XX:G1HeapWastePercent默认值5%),就会触发多轮Mixed GC。
3)元空间超过MetaspaceSize发生扩容
4)调用System.gc,且开启-XX:ExplicitGCInvokesConcurrent

(3)Mixed GC
1)第一阶段:并发标记
识别老年的region的活跃对象,计算出完全空闲的region(这个region里面都是垃圾对象),并将其回收。
2)第二阶段:垃圾回收
这个过程和年轻代回收的原理一样,mixed gc回收2部分垃圾,新生代 + 并发标记出来的垃圾对象region。

(4)Full GC
停止系统程序,然后采用单线程进行标记、清理和压缩整理,好空闲出来一批Region来供下一次MixedGC使用,这个过程是非常耗时的。(Shenandoah优化成多线程收集了)

​ 通常来说当某次回收后(大部分是YGC)堆内存占用达到45%左右,后面如果没降下去那很快就会进行mixed gc来整理老年代了。如果程序正常的话mixed gc频率应该是很低的,我们的项目可能还有点问题,但是一般也就30分钟一次。如果你的项目出现的频率很高,比如1-5分钟一次,那你就可能需要考虑是否出现了问题。频繁的mixed gc很可能说明了老年代内存得不到释放,同时可能还在增加,可能最后内存回收的速度跟不上分配的速度那就要full gc了,动不动就会十几秒,而且会不断进行下去,由于full gc是单线程STW的,此时服务基本就不可用了。所以full gc是要极力去避免的。

Humongous Region-巨形对象分区
一个 大小达到 甚至超过 分区大小一半 的 对象 称为 巨型对象(Humongous Object)
当线程为巨型分配空间时,不能简单在TLAB进行分配,因为巨型对象的移动成本很高,而且有可能一个分区不能容纳巨型对象
因此,巨型对象会直接在老年代分配,所占用的连续空间称为巨型分区(Humongous Region)

G1内部做了一个优化,一旦发现没有引用指向巨型对象,则可 直接在 年轻代收集周期中 被回收

巨型对象会独占一个、或多个连续分区,其中第一个分区被标记为开始巨型(StartsHumongous),相邻连续分区被标记为连续巨型(ContinuesHumongous)

由于无法享受TLAB带来的优化,并且确定一片连续的内存空间需要扫描整堆,因此确定巨型对象开始位置的成本非常高,如果可以,应用程序应避免生成巨型对象

设置H(Humongous Region)区的原因 是因为 对于堆中的大对象,默认会被分配到老年代,但如果它是一个短期存在的对象,由于老年代垃圾收集的频率较低,这个对象是不能及时被回收掉的,会对垃圾收集造成负面的影响

设置了H(Humongous Region)区,就能够及时回收,如果一个H区装不下一个大对象,则寻找连续的H区来存储,如果找不到连续的H区,就会启动Full GC

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值