zoukankan      html  css  js  c++  java
  • Java垃圾回收ygc代码模拟

    1、先来看看一个成功的按照预想进行了一次ygc的例子

    /**
     * ygc测试
     * -Xms10m -Xmx10m -Xmn5m -XX:+UseParallelGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log
           设置10m堆大小,年轻代和老年代各分5m,年轻代里伊甸区4m、两个幸存者区都是0.5m
     * */
    public class TestYoungGC2 {
        private static int _1MB = 1024*1024;
        public static void main(String[] args) {
            List cache = new ArrayList<byte[]>();
            //只能循环三次新增3M对象、到第4次就会发生ygc,因为eden本身不是完全4M可用的
            for (int i = 0; i < 4; i++){
                System.out.println("循环" + (i+1) + "开始");
                cache.add(new byte[_1MB]);
                cache.remove(0);
                System.out.println("循环" + (i+1) + "结束");
            }
    
            System.out.println("此时ygc回收了3M垃圾剩余1M对象、但这1M对象也失去了引用,下一次ygc将被回收");
            cache.add(new byte[2*_1MB]);
            System.out.println("此时又新分配2M对象到eden, 最后新生代3M+, 老年代接近0M");
        }
    }

    输出:

    循环1开始
    循环1结束
    循环2开始
    循环2结束
    循环3开始
    循环3结束
    循环4开始
    0.158: [GC (Allocation Failure) [PSYoungGen: 4046K->496K(4608K)] 4046K->644K(9728K), 0.0009270 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
    循环4结束
    此时ygc回收了3M垃圾剩余1M对象、但这1M对象也失去了引用,下一次ygc将被回收
    此时又新分配2M对象到eden, 最后新生代3M+, 老年代接近0M
    Heap
    PSYoungGen total 4608K, used 3688K [0x00000000ffb00000, 0x0000000100000000, 0x0000000100000000)
    eden space 4096K, 77% used [0x00000000ffb00000,0x00000000ffe1e368,0x00000000fff00000)
    from space 512K, 96% used [0x00000000fff00000,0x00000000fff7c020,0x00000000fff80000)
    to space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
    ParOldGen total 5120K, used 148K [0x00000000ff600000, 0x00000000ffb00000, 0x00000000ffb00000)
    object space 5120K, 2% used [0x00000000ff600000,0x00000000ff625010,0x00000000ffb00000)
    Metaspace used 2579K, capacity 4486K, committed 4864K, reserved 1056768K
    class space used 286K, capacity 386K, committed 512K, reserved 1048576K

     2、再看一个使用byte[]数组模拟ygc的例子,比较特殊

    -XX:NewSize=5m -XX:MaxNewSize=5m -XX:InitialHeapSize=10m -XX:MaxHeapSize=10m -XX:SurvivorRatio=8 -XX:+PrintGCDetails -XX:+PrintGCTimeStamps

    public class TestYoungGC {
        public static void main(String[] args) {
            byte[] array1 = new byte[1024*1024];
            array1 = new byte[1024*1024];
            array1 = new byte[1024*1024];
            array1 = null; //上面3M对象变成垃圾对象,可以被回收
            byte[] array2 = new byte[2*1024*1024]; //array2在eden分配不下,应该触发ygc
        }
    }

    运行结果:

    Heap
    PSYoungGen total 4608K, used 4096K [0x00000000ffb00000, 0x0000000100000000, 0x0000000100000000)
    eden space 4096K, 100% used [0x00000000ffb00000,0x00000000fff00000,0x00000000fff00000)
    from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
    to space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
    ParOldGen total 5120K, used 2048K [0x00000000ff600000, 0x00000000ffb00000, 0x00000000ffb00000)
    object space 5120K, 40% used [0x00000000ff600000,0x00000000ff800010,0x00000000ffb00000)
    Metaspace used 2577K, capacity 4486K, committed 4864K, reserved 1056768K
    class space used 286K, capacity 386K, committed 512K, reserved 1048576K

    意料之外的事情发生了:在向eden新分了3M对象之后,array2的2M对象并没有按照剧本来触发ygc回收先前的3M垃圾,然后再把这2M分到eden。

    从结果来看是这2M被直接分到老年代了,而eden因为刚好没有满,所以3M垃圾到最后也没有ygc回收掉。

    这种现象在 http://www.reins.altervista.org/java/gc1.4.2_faq.html 这篇文章有很好的解释:

    有两种情况对象是可能直接进入老年代,而不是尝试在新生代分配:

    1、你要分配的对象是一个大数组、且不包含其他对象的引用。

    2、你手工设置了-XX:PretenureSizeThreshold参数来指定直接进入老年代的对象的大小。

    所以为了导演出原来预期的结果,我们分别模拟下上面的两种方法

    1、首先,应该是array2太大了,先把它弄小点,最后试验出来:array2 设置小于等于2*1024*1024 -24触发ygc,大于等于2*1024*1024 -23不触发ygc,直接进入了老年代。

         所以代码修改如下:

    public class TestYoungGC {
        public static void main(String[] args) {
            byte[] array1 = new byte[1024*1024];
            array1 = new byte[1024*1024];
            array1 = new byte[1024*1024];
            array1 = null; //上面3M对象变成垃圾对象,可以被回收
            byte[] array2 = new byte[2*1024*1024 - 24]; //array2在eden分配不下,应该触发ygc
        }
    }

    输出:

    0.155: [GC (Allocation Failure) [PSYoungGen: 4046K->496K(4608K)] 4046K->652K(9728K), 0.0011635 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
    Heap
    PSYoungGen total 4608K, used 2585K [0x00000000ffb00000, 0x0000000100000000, 0x0000000100000000)
    eden space 4096K, 51% used [0x00000000ffb00000,0x00000000ffd0a540,0x00000000fff00000)
    from space 512K, 96% used [0x00000000fff00000,0x00000000fff7c040,0x00000000fff80000)
    to space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
    ParOldGen total 5120K, used 156K [0x00000000ff600000, 0x00000000ffb00000, 0x00000000ffb00000)
    object space 5120K, 3% used [0x00000000ff600000,0x00000000ff627010,0x00000000ffb00000)
    Metaspace used 2577K, capacity 4486K, committed 4864K, reserved 1056768K
    class space used 286K, capacity 386K, committed 512K, reserved 1048576K

    终于按照剧本来了!

    2、第二个方法,还是byte[] array2 = new byte[2*1024*1024],然后设置-XX:PretenureSizeThreshold=10m ,指定超过10M的对象才能直接进入老年代

         结果:

    Heap
    PSYoungGen total 4608K, used 4096K [0x00000000ffb00000, 0x0000000100000000, 0x0000000100000000)
    eden space 4096K, 100% used [0x00000000ffb00000,0x00000000fff00000,0x00000000fff00000)
    from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
    to space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
    ParOldGen total 5120K, used 2048K [0x00000000ff600000, 0x00000000ffb00000, 0x00000000ffb00000)
    object space 5120K, 40% used [0x00000000ff600000,0x00000000ff800010,0x00000000ffb00000)
    Metaspace used 2577K, capacity 4486K, committed 4864K, reserved 1056768K
    class space used 286K, capacity 386K, committed 512K, reserved 1048576K

    又不按剧本走了!  查阅网上资料,PretenureSizeThreshold这个参数只对Serial和ParNew两款收集器有效,笔者电脑在jdk8环境下默认的垃圾收集器是parallel,不认这个参数。

    所以调整一下收集器,再次修改jvm启动参数:

    -XX:NewSize=5m -XX:MaxNewSize=5m -XX:InitialHeapSize=10m -XX:MaxHeapSize=10m -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=10m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:PretenureSizeThreshold=10m -XX:+UseParNewGC

    运行:

    0.158: [GC (Allocation Failure) 0.158: [ParNew: 4046K->512K(4608K), 0.0012050 secs] 4046K->632K(9728K), 0.0012951 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
    Heap
    par new generation total 4608K, used 2601K [0x00000000ff600000, 0x00000000ffb00000, 0x00000000ffb00000)
    eden space 4096K, 51% used [0x00000000ff600000, 0x00000000ff80a558, 0x00000000ffa00000)
    from space 512K, 100% used [0x00000000ffa80000, 0x00000000ffb00000, 0x00000000ffb00000)
    to space 512K, 0% used [0x00000000ffa00000, 0x00000000ffa00000, 0x00000000ffa80000)
    tenured generation total 5120K, used 120K [0x00000000ffb00000, 0x0000000100000000, 0x0000000100000000)
    the space 5120K, 2% used [0x00000000ffb00000, 0x00000000ffb1e000, 0x00000000ffb1e000, 0x0000000100000000)
    Metaspace used 2577K, capacity 4486K, committed 4864K, reserved 1056768K
    class space used 286K, capacity 386K, committed 512K, reserved 1048576K
    Java HotSpot(TM) 64-Bit Server VM warning: Using the ParNew young collector with the Serial old collector is deprecated and will likely be removed in a future release

    终于如愿以偿按照剧本来了,最后变有个warning,意思是仅指定parNew作为新生代收集器的话、jvm默认给搭配的是serial old老年代收集器。要消除warning,手工指定一下别的老年代收集器即可,比如-XX:+UseConcMarkSweepGC

     参考资料:

    http://www.reins.altervista.org/java/gc1.4.2_faq.html

    https://www.spirithy.com/2019/04/01/trigger-jvm-full-gc-young-gc/

    https://www.jianshu.com/p/f7cde625d849

    https://www.cnblogs.com/hanlinhu/p/9487049.html

  • 相关阅读:
    李航博士:浅谈我对机器学习的理解
    数据挖掘过程中:数据预处理
    C++:构造函数和析构函数能否为虚函数
    PCA的数学原理
    奇异值分解(SVD) --- 几何意义
    3月机器学习在线班第六课笔记--信息熵与最大熵模型
    ML:交叉验证Cross-Validation
    LaTex的注释
    混合高斯模型
    NE2018届校招内推笔试——数据挖掘
  • 原文地址:https://www.cnblogs.com/lyhero11/p/13944971.html
Copyright © 2011-2022 走看看