垃圾收集器知识点总结-MaxTenuringThreshold

本文详细探讨了Java垃圾收集器中的MaxTenuringThreshold参数,解释了其实际含义并非对象必须经过15次GC后晋升,而是最大阈值。当对象在Survivor空间中达到一定比例时,阈值会动态调整,导致对象提前晋升到老年代。线上应用频繁GC的问题,通过分析源码和调整策略,揭示了阈值动态变化的原因。

MaxTenuringThreshold该参数用于控制对象经过GC多少次仍然存活后晋升到老年代的最大阈值,参数最大可配置为15,即对象最多经过15次GC后仍然存活就会晋升到老年代

记住!是最大!也就是说实际上不一定会经过15次才能晋升!这个值的JVM内部会进行动态计算然后动态改变的。

最近在线上有个应用GC非常频繁,MaxTenuringThreshold参数配置的是15,而以前对这个参数的理解是一定要经过15次后才会晋升,所以导致整个排查过程根本就不对,线上日志如下:

Desired survivor size 184549376 bytes, new threshold 1 (max 15)
- age   1:        369096960 bytes,        369096960 total
....下面进行GC,年轻代有很多对象全跑到了老年代

当时思考方向是:age为1,离15有很大的距离,不应该会晋升,老年代空间增加是因为大对象

这个思考方向真是大错特错了,因为对MaxTenuringThreshold理解错了。注意日志中有几个关键字

new threshold 1 (max 15)

意思是说阈值现在变成了1,而不是15!所以直接就晋升了!那么为什么会变成1呢,这个动态计算的算法是怎么样的,参考了一下一些大神的文章再看了下源码,终于发现原因所在,直接看源码会比较清晰,源码如下:

int ageTable::compute_tenuring_threshold(size_t survivor_capacity) {
  //TargetSurvivorRatio默认为50
  //desired_survivor_size = survivor的空间*50%
  size_t desired_survivor_size = (size_t)((((double) survivor_capacity)*TargetSurvivorRatio)/100);
  size_t total = 0;
  int age = 1;
  assert(sizes[0] == 0, "no objects with age zero should be recorded");
  while (age < table_size) {
    // 循环遍历所有年龄代的对象累加得到一个大小
    total += sizes[age];
    // 如果该大小大于desired_survivor_size,即survivor的空间*50%,那么退出循环
    if (total > desired_survivor_size) break;
    age++;
  }
  // 如果算出来的age大于MaxTenuringThreshold则使用MaxTenuringThreshold,否则使用计算出来的age
  int result = age < MaxTenuringThreshold ? age : MaxTenuringThreshold;

  if (PrintTenuringDistribution || UsePerfData) {
    if (PrintTenuringDistribution) {
      gclog_or_tty->cr();
      // 这里就是线上出现的那个日志所在的地方
      gclog_or_tty->print_cr("Desired survivor size %ld bytes, new threshold %d (max %d)",
        desired_survivor_size*oopSize, result, MaxTenuringThreshold);
    }
  //....
  }
  // 返回计算的年龄
  return result;
}

经过多次GC,存活的对象会在From和To两个地方来回的存放,而前提是两个空间能有足够的大小去存放这些数据,上述算法中,会计算每个年龄的大小,如果达到某个年龄后发现总大小以及大于Survivor的大小的50%,那么这时候就需要调整阈值,不能继续等到15次GC后才晋升,这样会导致Survivor的空间不足,所以调整阈值,让其尽快晋升

回到线上的那个例子,线上我记得是没配置TargetSurvivorRatio属性,那么按照50%算,survivor * 50%=184549376,在while循环中计算第一个年龄代=369096960>184549376,那么退出循环,此时计算得到的年龄为1,所以这个年龄的对象会提前晋升

参考文章:
https://www.jianshu.com/p/f91fde4628a5
https://blog.youkuaiyun.com/FoolishAndStupid/article/details/77596050?fps=1&locationNum=1

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值