zoukankan      html  css  js  c++  java
  • JVM-GC

    JVM GC(jdk1.7)

    一、minor gc

    ​ 发生在Eden、From、To之间,垃圾对象清理,存活对象从【Eden、From】复制到To,或者【Eden、To】复制到From

    image-20200514094712525

    image-20200514095435288

    image-20200514095503930

    晋升老年代

    ​ 在Survivor存活的对象,一般情况下(注意这里用词),年龄达到一定阈值,将晋升到老年代,阈值通过-XX:MaxTenuringThreshold设置,默认15,例如-XX:MaxTenuringThreshold=2

    image-20200514101549547

    大对象

    ​ -XX:PretenureSizeThreshold 大于这个设置值的对象可以直接在老年代分配对象

    ​ 这样做的好处是:避免在Eden区及两个Survivor区之前发生大量的内存复制

    image-20200514101747860

    动态对象年龄判定

    ​ 前面说关于GC年龄时用词是一般情况下,这里讲讲特殊情况

    ​ 并不一定要等到-XX:MaxTenuringThreshold,对象才进入老年代,在Survivor区相同的对象且年龄相同,总和大于Survivor的一半,也可以直接进入老年代

    ​ 下例:

    Dog1和Dog2年龄一样,且Dog1和Dog2年龄大于Survivor的一半,将直接进入老年代

    image-20200514102039391

    GC 日志

    • 例1

      survivor不够复制,直接进入老年代

    package com.test.allocation;
    
    /**
     * @author mdl
     * @date 2020/5/13 15:40
     */
    public class TestAllocation {
    
        private static final int _1MB = 1024 * 1024;
    
        /**
         * VM 参数: -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
         * Java堆大小20M,不可扩展,年轻代10M,年老代10M(Eden:8M, From:1M, To:1M)
         */
        public static void testAllocation(){
            byte[] allocation1;
            byte[] allocation2;
            byte[] allocation3;
            byte[] allocation4;
    
            allocation1 =new byte[2* _1MB];
            allocation2 =new byte[2* _1MB];
            allocation3 =new byte[2* _1MB];
            // 此时Eden已分配了6M,剩余2M不足以分配allocation4,触发Minor GC
            // GC期间,试图将存活的3个2M的对象复制到Survivor区,但是Survivor空间只有1M
            // 这时通过分配担保机制提前转义到老年代
            allocation4 =new byte[4* _1MB];
            // 所以此次GC结束:4M的allocation4进入Eden区,Survivor区空闲,老年代被占用6M
    
        }
    
    
        public static void main(String[] args) {
            TestAllocation.testAllocation();
        }
    
    }
    

    image-20200514102632928

    1. 分配allocation4时,发现Eden已经分配了6M(Eden有8M空间),不足以分配allocation4,发生minor gc

    2. gc期间,针对Eden,3个2M对象(allocation1、2、3)无法在Survivor分配,这时通过分配担保进入老年代

      ,老年代占用空间为6M

    3. 新生代资源利用为eden+from = 8192K + 1024K= 9216K,即9M,腾出了4M给allocation4利用

    • 例2

      大对象直接进入老年代

      package com.test.allocation;
      
      /**
       * 大对象直接进入老年代
       *
       * @author mdl
       * @date 2020/5/13 15:40
       */
      public class TestPretenuerSizeThreshold {
      
          private static final int _1MB = 1024 * 1024;
      
          /**
           * VM 参数: -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=3145728
           * Java堆大小20M,不可扩展,年轻代10M,年老代10M(Eden:8M, From:1M, To:1M)
           */
          public static void testAllocation(){
              byte[] allocation =new byte[4* _1MB];
          }
      
      
          public static void main(String[] args) {
              TestPretenuerSizeThreshold.testAllocation();
          }
      }
      

      image-20200514103215715

      ​ 从GC日志上看出,老年代的占用达到40%,总共10M,使用了4M,可以知道4M的allocation对象直接进入了老年代,这是因为PretenureSizeThreshold参数被设置为3MB

    • 例3

      相同的对象且年龄相同,空间利用率大于Survivor(From或To)的一半,gc时进入老年代(这其实是个误区)

      package com.test.allocation;
      
      /**
       * 动态对象年龄判定
       *
       * @author mdl
       * @date 2020/5/13 15:40
       */
      public class TestTenuringThreshold {
      
          private static final int _1MB = 1024 * 1024;
      
          /**
           * VM 参数: -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 -XX:+PrintTenuringDistribution
           * Java堆大小20M,不可扩展,年轻代10M,年老代10M(Eden:8M, From:1M, To:1M)
           */
          public static void testAllocation() {
              byte[] allocation1 = new byte[_1MB / 4];
              // allocation1+ allocation2 大于survivor空间的一半了
              byte[] allocation2 = new byte[_1MB / 4];
      
              byte[] allocation3 = new byte[4 * _1MB];
              // 第一次Minor GC allocation1和allocation2年龄为1
              byte[] allocation4 = new byte[4 * _1MB];
              // 将allocation4置空,在下一次回收将被回收
              allocation4 = null;
              // 第二次Minor GC allocation1和allocation2进入老年代
              allocation4 = new byte[4 * _1MB];   // 1
          }
      
      
          public static void main(String[] args) {
              TestTenuringThreshold.testAllocation();
          }
      
      }
      
      
      

      如果注释掉代码1处

      image-20200514105026730

      分析:

      只发生一次minor gc,allocation4分配前,eden已占用1/2 M+ 4M=4.5M,还剩1.5M,不足以分配allocation4

      allocation3进入老年代,allocation1、2进入from,eden分配allocation4

      至于为什么结果from是100%,暂时未可知

      释放代码1处

      image-20200514111557614

      survivor空闲,而老年代空间增加了10%,allocation1、2进入了老年代了

      当然以上推断【对象相同且年龄相同,空间大约survivor一半】其实是有问题的

      我们假如:

      1. MaxTenuringThreshold为15

      2. 年龄1对象30%

      3. 年龄2对象30%

      4. 年龄3对象40%

        可以看到survivor已经100%了,但是对象就不晋升,导致老年代有空间,对象就停留在年轻代,这明显与jvm的表现不符。

        uint ageTable::compute_tenuring_threshold(size_t survivor_capacity) {
        	//survivor_capacity是survivor空间的大小,TargetSurvivorRatio:目标存活率,默认50
          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;
        	...
        }
        

        代码的意思大概总结为:

        从age为1开始,累加每一个年龄段对象大小,如果大小超过survivor空间一半,取age与MaxTenuringThreshold的最小值暂记为result,大于等于result的age将进入老年代

        年龄1(30%)+年龄2(30%)> 50%,那么大于等于年龄2的将晋升到老年代

        参考:

        https://www.cnblogs.com/lovellll/p/10246073.html

        《深入理解Java虚拟机》

    每一步脚印都要扎得深一点!
  • 相关阅读:
    Spring的IOC原理(转载)
    谈谈对Spring IOC的理解
    Servlet3.0学习总结(四)——使用注解标注监听器(Listener)
    Servlet3.0学习总结(三)——基于Servlet3.0的文件上传
    Servlet3.0学习总结(二)——使用注解标注过滤器(Filter)
    Servlet3.0学习总结(一)——使用注解标注Servlet
    XML学习总结(二)——XML入门
    XML学习总结(一)——XML介绍
    孤傲苍狼的博客园
    android 布局之滑动探究 scrollTo 和 scrollBy 方法使用说明
  • 原文地址:https://www.cnblogs.com/bloodthirsty/p/12887487.html
Copyright © 2011-2022 走看看