• jvm垃圾回收(区域、时间、算法)


    1、进行垃圾回收的区域

    (1)堆

    (2)方法区

    栈是线程的私有数据,所以不进行垃圾回收

    2、垃圾回收的时间

    对这个对象不再引用的时候

    public class ReferenceCountingGC {
        private static final int MB=1024*1024;
        public Object instance=null;
        private byte[] size=new byte[2*MB];
    
        public static void main(String[] args) {
            ReferenceCountingGC referenceCountingGC1=new ReferenceCountingGC();
            ReferenceCountingGC referenceCountingGC2=new ReferenceCountingGC();
            //循环引用
            referenceCountingGC1.instance=referenceCountingGC2;
            referenceCountingGC2.instance=referenceCountingGC1;
    
            referenceCountingGC1=null;
            referenceCountingGC2=null;
    
            System.gc();;
        }
    }

    [Full GC (System.gc()) 
    [Tenured: 0K->640K(86016K), 0.0045506 secs] 6862K->640K(124736K), 
    [Metaspace: 3223K->3223K(1056768K)], 0.0046551 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
    Heap
     def new generation   total 38784K, used 345K [0x0000000082200000, 0x0000000084c10000, 0x00000000ac150000)
      eden space 34496K,   1% used [0x0000000082200000, 0x0000000082256548, 0x00000000843b0000)
      from space 4288K,   0% used [0x00000000843b0000, 0x00000000843b0000, 0x00000000847e0000)
      to   space 4288K,   0% used [0x00000000847e0000, 0x00000000847e0000, 0x0000000084c10000)
     tenured generation   total 86016K, used 640K [0x00000000ac150000, 0x00000000b1550000, 0x0000000100000000)
       the space 86016K,   0% used [0x00000000ac150000, 0x00000000ac1f0040, 0x00000000ac1f0200, 0x00000000b1550000)
     Metaspace       used 3230K, capacity 4496K, committed 4864K, reserved 1056768K
      class space    used 350K, capacity 388K, committed 512K, reserved 1048576K

    (1)引用计数

    给对象添加一个引用计数器,每当这个对象进行一次引用,计数器就加1;每当引用失效的时候,计数器就减1。当这个计数器等于0的时候,表示这个对象不会再被引用了,可以作为垃圾进行回收。但是,存在循环引用的时候会导致内存泄漏

     3、java的引用类型

    (1)强引用

    Object obj=new Object();这样的常规引用,只要引用还在就永远不会回收对象,在代码中有明显的new Object()这类引用,只要这种引用还在,垃圾回收器就不会回收它。就算内存不够,抛出OutOfMrmory异常也不会回收对象。

    (2)弱引用

    生存到下一次垃圾回收之前,无论当前内存是否够用,都回收掉被弱引用关联的对象

    (3)软引用

    在发生内存溢出之前,进行回收,如果这次回收之后还没有足够的内存,则报OOM。如果内存够用的情况下,不会回收,如果内存不够的话进行

    (4)虚引用

    不会对对象的生命周期有任何影响,也无法通过它得到对象的实例,唯一的作用也就是在垃圾回收之前收到一个系统通知

    4、可达性分析算法

    (1)可达与不可达

    可达:

     不可达:

    此时对象已经不再被引用了,也就是说可以被回收了。

    (2)在java语言中,可以作为GC Roots的对象(肯定不会变成垃圾被回收的对象)包括下面几种:

    虚拟机栈(栈帧中的本地变量表)中引用的对象

    方法区中类静态属性引用的对象

    方法区中常量引用的对象

    本地方法栈中JNI(即一般说的Native方法)引用的对象

    5、标记、清除算法

    (1)概念:

    该算法包括“标记”和“清除”两个阶段:首先标记出所有要回收的对象,在标记完成后统一回收所有被标记的对象。

    (2)缺点:

    效率问题,标记和清除的效率都不高

    空间问题,标记和清除之后会产生大量不连续的内存碎片

    6、复制算法

    (1)概念

    将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块内存用完了,就将还存活的对象复制到另外一块上面,然后再把已经使用过的内存空间一次清理掉

    (2)缺点

    内存缩小为原来的一半,内存利用率太低(还要留出另外一半等着复制的时候使用)

    (3)优化

    为了克服该算法的缺点对该算法进行了改进,内存区域不再是按照1:1去划分,而是将内存划分为8:1:1三部分,较大那份内存叫Eden区,其余是两块较小的内存区叫Survior区。每次都会优先使用Eden区,若Eden区满,就将对象复制到第二块内存区上,然后清除Eden区,如果此时存活的对象太多,以至于Survivor区不够,会将这些对象通过分配担保机制复制到老年代中。(java堆又分为新生代和老年代)

    7、标记整理算法

    (1)概念

    标记过程依旧和“标记清除算法”一样,但是后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后把其他的内存区域清理掉。可以消除内存碎片,但是整理的时候会降低算法的效率

    8、分代算法

    (1)概念

    根据对象存活周期的不同将内存划分为几块

    (2)回收原则

    一般是把java堆分为新生代和老年代,这样可以根据各个年代的特点采用最适合的收集算法。在新生代中,每次垃圾回收的时候都会回收掉大量的对象,只有少量的对象存活,此时,选用复制算法,只需要付出少量存活对象的复制成本即可完成对象的收集,而老年代中因为对象的存活率高,没有额外空间进行分配担保,就必须使用“标记清理”或“标记整理”算法进行回收。新生代垃圾回收较为频繁,老年代不频繁

    堆:

     经过几次回收之后会进入老年代

    (3)分区

    伊甸园(Eden):这是对象最初诞生的区域,并且对大多数对象来说,这里是它们唯一存在过的区域。当伊甸区满的时候会触发Minor-GC,采用复制算法将伊甸区的幸存对象和from存活的对象复制到幸存区的to区域,存活的对象年龄加一,当在新生代的生存次数累积到一定程度(最大寿命是15,存储寿命的地方是4bit)可以进入老年代。

    幸存者乐园(Survivor):从伊甸园幸存下来的对象会被挪到这里。

    终身颐养园(Tenured):这是足够老的幸存对象的归宿。年轻代收集(Minor-GC)过程是不会触及这个地方的。当年轻代收集不能把对象放进终身颐养园时(老年代空间不足,先尝试触发Minor-GC,当Minor-GC也不能腾出足够的空间的时候),就会触发一次完全收集(Major-GC或Full-GC),这里可能还会牵扯到压缩,以便为大对象腾出足够的空间。

    如果老年代仍旧不能分配出来空间,就会报异常

    (4)STW

    当进行Minor-GC或Full-GC的时候会触发stop the world把用户的其他线程暂停掉,因为Minor-GC的时候牵涉到对象的复制,如果还有其他线程操作该对象就会引起混乱。Full-GC的时间更长,因为它采用标记整理算法,速度更慢。

    (5)虚拟机相关参数

    9、GC类型

    (1)保守式GC

    在回收的时候,不对数据进行记录,而是扫描内存,效率低

    (2)半保守式GC

    在类里面记录信息,然后扫描

    (3)精确式GC

    OopMap存放运行信息(类型、地址等),回收的时候只需查询Map即可

    10、安全点:safepoint

    (1)概念

    在OopMap的协助下,HotSpot可以快速且准确地完成GC Roots的枚举,但如果每一条指令都生成对应的OopMap那将会需要大量的额外空间,这样GC的空间成本将会变得很高

    (2)抢占式中断

    在GC发生时,首先把所有的线程全部中断,如果发现有中断的地方不在安全点上,就恢复线程,让它跑到安全点上,必须要等到Java线程都进入到safepoint的时候VMThread才能开始执行GC

    位置:

    循环的末尾(防止大循环的时候一直不进入safepoint,而其他线程在等待它进入safepoint)
    方法返回前
    调用方法的call之后
    抛出异常的位置

    缺点:处于sleep或wait的时候不能到达安全点

    (3)主动式中断

    当GC需要中断线程的时候,不直接对线程进行操作,仅仅简单地设置一个标志,发现中断标志为真的时候就自己中断挂起

    11、相关参数

    与垃圾回收相关的JVM参数:

    -Xms / -Xmx --- 堆的初始大小 / 堆的最大大小
    
    -Xmn --- 堆中年轻代的大小
    
    -XX:-DisableExplicitGC --- 让System.gc()不产生任何作用
    
    -XX:+PrintGCDetail --- 打印GC的细节
    
    -XX:+PrintGCDateStamps --- 打印GC操作的时间戳

    12、程序分析

    (1)jvm参数

     最大堆空间:20M

    打印GC详情

    新生代:10M

    垃圾回收器:SerialGC

    (2)不对存储区进行任何操作:

    public class Test3 {
        private static final int _512KB=512*1024;
        private static final int _1MB=1024*1024;
        private static final int _6MB=6*1024*1024;
        private static final int _7MB=7*1024*1024;
        private static final int _8MB=8*1024*1024;
    
        public static void main(String[] args) {
    
        }
    }

    测试结果:

    Heap
     def new generation   total 9216K, used 2025K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
      eden space 8192K,  24% used [0x00000000fec00000, 0x00000000fedfa778, 0x00000000ff400000)
      from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
      to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
     tenured generation   total 10240K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
       the space 10240K,   0% used [0x00000000ff600000, 0x00000000ff600000, 0x00000000ff600200, 0x0000000100000000)
     Metaspace       used 3224K, capacity 4496K, committed 4864K, reserved 1056768K
      class space    used 349K, capacity 388K, committed 512K, reserved 1048576K

    堆内存:

    新生代:def new generation

    老年代:tenured generation

    (3)将7M的对象放入堆内存:

    public class Test3 {
        private static final int _512KB=512*1024;
        private static final int _1MB=1024*1024;
        private static final int _6MB=6*1024*1024;
        private static final int _7MB=7*1024*1024;
        private static final int _8MB=8*1024*1024;
    
        public static void main(String[] args) {
            ArrayList<byte[]> list=new ArrayList<>();
            list.add(new byte[_7MB]);
        }
    }

    测试:

    [GC (Allocation Failure) [DefNew: 1861K->641K(9216K), 0.0026541 secs] 1861K->641K(19456K), 0.0283396 secs] 
    [Times: user=0.00 sys=0.00, real=0.03 secs] Heap def new generation total 9216K, used 8055K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000) eden space 8192K, 90% used [0x00000000fec00000, 0x00000000ff33d8c0, 0x00000000ff400000) from space 1024K, 62% used [0x00000000ff500000, 0x00000000ff5a0710, 0x00000000ff600000) to space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000) tenured generation total 10240K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000) the space 10240K, 0% used [0x00000000ff600000, 0x00000000ff600000, 0x00000000ff600200, 0x0000000100000000) Metaspace used 3229K, capacity 4496K, committed 4864K, reserved 1056768K class space used 350K, capacity 388K, committed 512K, reserved 1048576K

    因为发生了Minor-GC,幸存代的空间也被占用了

    (4)放入8M的对象

    public class Test3 {
        private static final int _512KB=512*1024;
        private static final int _1MB=1024*1024;
        private static final int _6MB=6*1024*1024;
        private static final int _7MB=7*1024*1024;
        private static final int _8MB=8*1024*1024;
    
        public static void main(String[] args) {
            ArrayList<byte[]> list=new ArrayList<>();
            list.add(new byte[_7MB]);
            list.add(new byte[_512KB]);
            list.add(new byte[_512KB]);
    
        }
    }

    测试:

    [GC (Allocation Failure) [DefNew: 1861K->614K(9216K), 0.0029223 secs] 1861K->614K(19456K), 0.0029845 secs] 
    [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [DefNew: 8621K->514K(9216K), 0.0059726 secs] 8621K->8292K(19456K), 0.0060135 secs]
    [Times: user=0.00 sys=0.00, real=0.01 secs] Heap def new generation total 9216K, used 1191K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000) eden space 8192K, 8% used [0x00000000fec00000, 0x00000000feca93e0, 0x00000000ff400000) from space 1024K, 50% used [0x00000000ff400000, 0x00000000ff480848, 0x00000000ff500000) to space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000) tenured generation total 10240K, used 7778K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000) the space 10240K, 75% used [0x00000000ff600000, 0x00000000ffd98a48, 0x00000000ffd98c00, 0x0000000100000000) Metaspace used 3214K, capacity 4496K, committed 4864K, reserved 1056768K class space used 348K, capacity 388K, committed 512K, reserved 1048576K

    此时,老年代也有占用,一部分对象已经晋升到老年代

    (5)大对象直接晋升老年代(新生代放不下)

    public class Test3 {
        private static final int _512KB=512*1024;
        private static final int _1MB=1024*1024;
        private static final int _6MB=6*1024*1024;
        private static final int _7MB=7*1024*1024;
        private static final int _8MB=8*1024*1024;
    
        public static void main(String[] args) {
            ArrayList<byte[]> list=new ArrayList<>();
            list.add(new byte[_8MB]);
        }
    }

    测试:

    Heap
     def new generation   total 9216K, used 2025K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
      eden space 8192K,  24% used [0x00000000fec00000, 0x00000000fedfa778, 0x00000000ff400000)
      from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
      to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
     tenured generation   total 10240K, used 8192K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
       the space 10240K,  80% used [0x00000000ff600000, 0x00000000ffe00010, 0x00000000ffe00200, 0x0000000100000000)
     Metaspace       used 3229K, capacity 4496K, committed 4864K, reserved 1056768K
      class space    used 350K, capacity 388K, committed 512K, reserved 1048576K

    无GC,直接进入老年代

    (6)放入两个8M的对象:新生代和老年代都放不下

    public class Test3 {
        private static final int _512KB=512*1024;
        private static final int _1MB=1024*1024;
        private static final int _6MB=6*1024*1024;
        private static final int _7MB=7*1024*1024;
        private static final int _8MB=8*1024*1024;
    
        public static void main(String[] args) {
            ArrayList<byte[]> list=new ArrayList<>();
            list.add(new byte[_8MB]);
            list.add(new byte[_8MB]);
        }
    }

    测试:

    [GC (Allocation Failure) [DefNew: 1861K->616K(9216K), 0.0016678 secs][Tenured: 8192K->8807K(10240K), 0.0274991 secs] 10053K->8807K(19456K), [Metaspace: 3201K->3201K(1056768K)], 0.0498951 secs] [Times: user=0.00 sys=0.02, real=0.05 secs] 
    [Full GC (Allocation Failure) [Tenured: 8807K->8789K(10240K), 0.0025803 secs] 8807K->8789K(19456K), [Metaspace: 3201K->3201K(1056768K)], 0.0026219 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at pers.zhb.test.Test3.main(Test3.java:15)
    Heap
     def new generation   total 9216K, used 410K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
      eden space 8192K,   5% used [0x00000000fec00000, 0x00000000fec66800, 0x00000000ff400000)
      from space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
      to   space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
     tenured generation   total 10240K, used 8789K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
       the space 10240K,  85% used [0x00000000ff600000, 0x00000000ffe95630, 0x00000000ffe95800, 0x0000000100000000)
     Metaspace       used 3255K, capacity 4496K, committed 4864K, reserved 1056768K
      class space    used 353K, capacity 388K, committed 512K, reserved 1048576K
  • 相关阅读:
    Hadoop学习笔记
    Hadoop学习笔记 -伪分布式
    SSH 连接报错总结
    Hadoop学习笔记
    Trie 前缀树/字典树
    解数独(Leetcode-37 / HDU-1426)/回溯/状态压缩
    MyBatis 多表关联查询
    python_37期自动化【lemon】
    api课堂笔记_day14
    api课堂笔记_day12&day13
  • 原文地址:https://www.cnblogs.com/zhai1997/p/12606874.html
Copyright © 2020-2023  润新知