【对象晋升老年代的条件】

年轻代

1.正常晋升

eden 满了触发一次 minor GC回收 把幸存对象放入幸存者s0 或者幸存者s1且把年轻值超过15的对象(通过-XX:MaxTenuringThreshold=1 设置)放入老年代

2.大对象晋升

当幸存者区剩余内存放不下回收的对象大小时直接把对象放入老年代(大对象直接进入老生代)

3.动态年龄晋升

幸存者区中所有对象的年龄从小到大进行累加,当加入某个年龄段后,累加对象大小如果大于等于Desired survivor size(S区期望大小),则重新计算threshold(晋升阈值),age等于累加触发的年龄,以age和MaxTenuringThreshold两者的最小值为准。 min(age, MaxTenuringThreshold)=threshold

jvm 动态年龄相关代码

uint ageTable::compute_tenuring_threshold(size_t survivor_capacity) {
    //survivor_capacity是survivor空间的大小
  size_t desired_survivor_size = (size_t)((((double) survivor_capacity)*TargetSurvivorRatio)/100);
  size_t total = 0;
  uint age = 1;
  while (age < table_size) {
    total += sizes[age];//sizes数组是每个年龄段对象大小
    if (total > desired_survivor_size) break;
    age++;
  }
  uint result = age < MaxTenuringThreshold ? age : MaxTenuringThreshold;
    ...
}

1.通过这个比率来计算一个期望值,desired_survivor_size 。
2.然后用一个total计数器,累加每个年龄段对象大小的总和。
3.当total大于desired_survivor_size 停止。
4.然后用当前age和MaxTenuringThreshold 对比找出最小值作为结果。

总体表征就是,年龄从小到大进行累加,当加入某个年龄段后,累加和超过survivor区域*TargetSurvivorRatio的时候,就从这个年龄段往上的年龄的对象进行晋升。

举例
1.MaxTenuringThreshold为15
2.年龄1的对象占用了33%
3.年龄2的对象占用33%
4.年龄3的对象占用34%。
年龄1的占用了33%,年龄2的占用了33%,累加和超过默认的TargetSurvivorRatio(50%),年龄2和年龄3的对象都要晋升

举例
jvm:当Eden区为40M,S0和S1为5M
参数:-XX:TargetSurvivorRatio=60 -XX:MaxTenuringThreshold=3

s0、s1(5M)*TargetSurvivorRatio(60%)=Desired survivor size(3M)
(age, MaxTenuringThreshold)=threshold
(1,3)=1

//-Xmx200M -Xmn50m -XX:TargetSurvivorRatio=60 -XX:+PrintGCDetails -XX:+PrintGCDateStamps 
//-XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:MaxTenuringThreshold=3
//最小堆为50M,默认SurvivorRatio为8,那么可以知道Eden区为40M,S0和S1为5M

public class App {
  public static void main(String[] args) throws InterruptedException {

        // main方法作为主线程,变量不会被回收
       // 达到TargetSurvivorRatio这个比例指定的值,即 5M(S区)*60%(TargetSurvivorRatio)=3M(Desired survivor size)
        byte[] byte4 = new byte[1 * 1024 * 1024];
        byte[] byte5 = new byte[1 * 1024 * 1024];
        byte[] byte6 = new byte[1 * 1024 * 1024];

        // 这次ygc时, 由于s区已经占用达到了60%(-XX:TargetSurvivorRatio=60),
        // 所以会重新计算对象晋升的min(age, MaxTenuringThreshold) = 1
        YGC(40);
        Thread.sleep(3000);

        // 由于前一次ygc时算出age=1, 所以这一次再ygc时, byte4, byte5, byte6就要晋升到Old,
        // 而不需要等MaxTenuringThreshold这么多次, 此次ygc后, s0(from)/s1(to)的空间再次为0, 对象全部晋升到old
        YGC(40);
        Thread.sleep(3000);
      }

   //塞满Eden区,局部变量会被回收,作为触发GC的小工具
    private static void YGC(int edenSize){
        for (int i = 0 ; i < edenSize ; i ++) {
            byte[] byte1m = new byte[1 * 1024 * 1024];
        }
    }

 }

优点
为什么要动态的计算tenuring threshold的值呢?
假设有很多年龄还未达到TenuringThreshold的对象依旧停留在survivor区,这样不利于新对象从eden晋升到survivor。因此设置survivor区的目标使用率,当使用率达到时重新调整TenuringThreshold值,让对象尽早的去old区。

4.空间担保GC

在发生Minor GC之前,虚拟机会检查老年代最大可用的连续空间是否大于新生代所有对象的总空间,
如果大于,则此次Minor GC是安全的
如果小于,则虚拟机会查看HandlePromotionFailure(JDK7及以后这个参数就失效了)设置值是否允许担保失败。如果HandlePromotionFailure=true,那么会继续检查老年代最大可用连续空间是否大于历次晋升到老年代的对象的平均大小,如果大于,则尝试进行一次Minor GC,但这次Minor GC依然是有风险的;如果小于或者HandlePromotionFailure=false,则改为进行一次Full GC。
在JDK7后HandlePromotionFailure参数不在起作用规则变为只要老年代的连续空间大于新生代对象总大小或者历次晋级的平均大小就会进行Minor GC,否则将进行Full GC

参考
https://www.cnblogs.com/zjfjava/p/13861797.html
https://www.cnblogs.com/shoshana-kong/p/14647579.html

posted @   汪兔斯瑞佛  阅读(349)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示