• JVM垃圾回收?看这一篇就够了!


    深入理解JVM垃圾回收机制

    1、垃圾回收需要解决的问题及解决的办法总览

    • 1、如何判定对象为垃圾对象
      • 引用计数法
      • 可达性分析法
    • 2、如何回收
      • 回收策略
        • 标记-清除算法
        • 复制算法
        • 标记-整理算法
        • 分带收集算法
      • 垃圾回收器
        • serial
        • parnew
        • Cms
        • G1
    • 3、何时回收

    下面就是如何判定对象为垃圾对象


    2、引用计数法

    在对象中添加一个引用计数器,当有地方引用这个对象的时候,引用技术器得值就+1,当引用失效的时候,计数器得值就-1
    算法缺点:当某个引用被收集时,下个引用并不会清0,因此不被回收造成内存泄露。

    下面我们运行实例代码来看,JVM在循环引用时,是否能被收集(如果回收了就说明垃圾回收器用的不是引用计数法)。

    如果想打印日志信息,请填入如下参数。

    -verbose:gc -XX:+PrintGCDetails 
    

    其中我们需要将每个对象的所占内存扩大,因此我们声明一个大点的空间。

    测试实验代码如下:

    public class A {
    	private Object instance;
    	public A() {
    		byte[] m = new byte[20*1024*1024];
    	}
    	public static void main(String[] args) {
    		A a1 = new A();
    		A a2 = new A();
    		a1.instance=a2;
    		a2.instance=a1;
    		
    		a1=null;
    		a2=null;
    		System.gc();
            //parallel 默认采用的垃圾回收器
    	}
    }
    
    

    运行结果如下所示:

    [GC (System.gc()) [PSYoungGen: 22446K->648K(37888K)] 42926K->21136K(123904K), 0.0011193 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
    [Full GC (System.gc()) [PSYoungGen: 648K->0K(37888K)] [ParOldGen: 20488K->519K(86016K)] 21136K->519K(123904K), [Metaspace: 2632K->2632K(1056768K)], 0.0074751 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
    Heap
    PSYoungGen total 37888K, used 328K [0x00000000d6000000, 0x00000000d8a00000, 0x0000000100000000)
    eden space 32768K, 1% used [0x00000000d6000000,0x00000000d6052030,0x00000000d8000000)
    from space 5120K, 0% used [0x00000000d8000000,0x00000000d8000000,0x00000000d8500000)
    to space 5120K, 0% used [0x00000000d8500000,0x00000000d8500000,0x00000000d8a00000)
    ParOldGen total 86016K, used 519K [0x0000000082000000, 0x0000000087400000, 0x00000000d6000000)
    object space 86016K, 0% used [0x0000000082000000,0x0000000082081fd8,0x0000000087400000)
    Metaspace used 2638K, capacity 4486K, committed 4864K, reserved 1056768K
    class space used 281K, capacity 386K, committed 512K, reserved 1048576K

    这里我们会看到 22446K->648J这里,我们的对象被回收了,这就说明我们JVM采用的垃圾回收算法并不是引用计数法。


    3、可达性分析法

    算法如名,可达性分析法就是从GCroot结点开始,看能否找到对象。

    GCroot结点开始向下搜索,路径称为引用链,当对象没有任何一条引用链链接的时候,就认为这个对象是垃圾,并进行回收。

    那么什么是GCroot呢(虚拟机在哪查找GCroot)。

    • 虚拟机栈(局部变量表)
    • 方法区的类属性所引用的对象。
    • 方法区中常量所引用的对象。
    • 本地方法栈中引用的对象。

    目前主流JVM采用的垃圾判定算法就是可达性分析法。


    至此垃圾判定算法结束

    垃圾回收算法开始


    4、标记-清除算法

    存在的问题:

    • 效率问题。
    • 内存小块过多。
      如图所示:黄色的就是被标记清除的。清除后会发现有很多多余的小块。


    5、复制算法

    下面是java内存常规划分

    • (线程共有)堆内存 方法区
    • (栈内存 本地方法栈) 程序计数器

    下面是堆内存的划分

    • 新生代
      • Eden 伊甸园
      • Survivor 存活区
      • Tenured Gen 养老区
    • 老年代

    下面就是过程:

    被标记的黑色就是需要回收的

    将白色区域复制下面,然后清空上面的


    这样就完成了内存的连续分配,但是引来一个问题。
    每次只能使用一半的内存。是不是有点少。。

    为了解决这个问题,我们对内存就进行了划分。
    我们对内存分为了三块区域。

    内存区域 所占百分比
    Eden 80%
    survivor 10%
    Tenured Gen 一点点

    复制算法,我们需要将上面的思路,将Eden中需要回收的对象放到Survivor,然后清除。
    也就是俩个Survivor中进行复制与清除。

    这里我们即提高了效率,又减少了内存分配。

    如果Survivor不够放,那就扔到老年代里,或者其他方法,反正有内存担保。


    6、标记-整理算法

    复制算法主要针对新生代内存收集方法。
    标记-整理算法主要针对的是老年代内存收集方法。

    主要步骤:标记-整理-清除

    如下图所示

    然后将右面的进行删除计科达到回收效果。

    7、分代收集算法

    分代收集算法是根据内存的分代选择不同的算法。
    对于新生代,一般选择复制算法。
    对于老年代,一般选择标记-整理-清除算法。

    显而易见,这是上面俩种算法的优点糅合在一起的应用。


    至此我们总结了所有垃圾回收算法。
    下面就是各种出名的垃圾收集器


    8、Serial收集器

    特点:

    • 出现的最早的,发展最悠久的垃圾收集器。
    • 单线程垃圾收集器。
    • 主要针对新生代内存进行收集

    运行机制如下所示

    缺点:慢

    用处:在客户端上运行还是比较有效。没有线程的开销,所以在客户端还是比较好用的。


    9、ParNew收集器

    特点:

    • 由单线程变成了多线程垃圾收集器。
    • 如果要用CMS进行收集的话,最好采用ParNew收集器。

    实现原理都是复制算法。

    缺点:

    • 性能较慢

    10、Parallel Scavenge 收集器

    主用算法:复制算法(新生代收集器)

    吞吐量 = (执行用户代码消耗的时间)/(执行用户代码的时间)+ 垃圾回收时所占用的时间

    优点:吞吐量优化(CPU用于运行用户代码的时间与CPU消耗的总时间的比值)

    关于控制吞吐量的参数如下

    -XX:MaxGCPauseMills #垃圾收集器的停顿时间
    -XX:GCTimeRatio #吞吐量大小
    
    

    当停顿时间过小时,内存对应变小,回收的频率增大。因此第一个参数需要设置的合理才比较好。
    第二个参数值越大,吞吐量越大,默认是99,(垃圾回收时间最多只能占到1%)

    总的来说:客户端可用,服务端最好不用。


    11、CMS收集器(Concurrent Mark Sweep)

    采用算法:标记清除算法。

    • 工作过程:
      • 初始标记
      • 并发标记
      • 重新标记
      • 并发清理
    • 优点:
      • 并发收集
      • 低停顿
    • 缺点:
      • 占用大量的CPU资源
      • 无法处理浮动垃圾
      • 出现ConcurrentMode Failure
      • 空间碎片

    CMS是一个并发的收集器。

    目标是:减少延迟,增加响应速度
    执行效果如下所示:

    • 初始标记
      • 可达性分析法
    • 重新标记
      • 为了修正并发期间,因对象重新运作而修正
    • 并发清理
      • 直接清除了

    12、G1收集器(面向服务端)

    最牛的垃圾收集器。

    • 历史
      -2004年Sun发表了第一篇G1的论文,到2006年左右,在JDK6内集成进去了。JDK7才放出来。
    • 优势
      • 集中了前面所有收集器的优点
      • G1能充分利用了多核的并行特点,能缩短停顿时间。
      • 分代收集(分成各种Region)
      • 空间整合(类似于标记清理算法)
      • 可预测的停顿()。
    • 步骤
      • 初始标记
      • 并发标记
      • 最终标记
      • 筛选回收

    13、小结:

    至此我们就已经掌握了大部分GC的知识。这可不是一个小工程,希望要好好吸收知识。。

  • 相关阅读:
    IDEA中Maven依赖下载失败解决方案
    Java 泛型 泛型的约束与局限性
    年轻就该多尝试,教你20小时Get一项新技能
    Java入门
    2020全球C++及系统软件技术大会成功落下帷幕
    hashMap底层源码浅析
    hashMap底层源码浅析
    RabbitMQ (二) 简单队列
    RabbitMQ (一) 简介和基本概念
    Linux 关闭/开启密码登录(仅证书登录)
  • 原文地址:https://www.cnblogs.com/godoforange/p/11552865.html
Copyright © 2020-2023  润新知