关于GC

作者: cf6bfeab5260 | 来源:发表于2019-04-18 16:01 被阅读0次

    1 准备工作

    1.1 了解垃圾回收算法

    1.1.1 标记-清除算法

    标记-清楚算法是最基础的GC算法,过程分为了两个阶段:标记、清除。

    • 在标记阶段:会从对象根对象找到可达对象,然后从这些可达对象再找它们的可达对象,直到没有为止:

    根对象:比如GC发生时,程序正在运行中的方法的局部变量。

    image.png

    比如:根对象的可达对象B会被标记,B对象的可达对象E也会被标记。

    • 清除阶段:把所有没有被标记的对象进行回收清除。

    标记清楚算法有明显的缺点:

    1. 为了让标记更准确,在标记阶段,会让应用处于停止(也就是人们常说的stop the world)。
    2. 在清除了过后,会产生大量的不连续碎片空间。让空间利用率下降。

    1.1.2 复制算法

    复制算法的原理是:将可用内存按容量划分为大小相等的两块,每次使用其中的一块。当这一块的内存用完了,就将还存活的对象复制到另一块内存上,然后把这一块内存所有的对象一次性清理掉。


    image.png

    回收后:


    image.png

    很明显复制算法解决了标记-清除算法的两个问题,但是缺点也很明显:

    • 浪费了一半内存
    • 对于对象存活率较高的场景(比如老年代),复制的代价也比较大。

    1.1.3 标记-整理算法

    标记-整理算法的过程,是让存活下来的对象,往右移动,然后清楚掉前面的部分:


    image.png

    回收后:


    image.png

    1.1.4 标记-清理(改进版)

    初始标记(CMS inital mark):需要“stop the world”,但只标记一下GC Roots能直接关联的对象,速度很快。
    并发标记(CMS concurrent mark):是GC Roots Tracing的过程,花费时间长
    重新标记(CMS remark):*需要“stop the world”,是为了修正并发标记期间因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。
    并发清除(CMS concurrent sweep):是并发清除无用对象。

    这个算法的优点在于停顿时间短,缺点是需要消耗一部分cpu资源。

    2 收集器

    了解完了算法,我们开始说GC收集器:


    image.png

    这个图表明了几个意思:

    • Serial、ParNew、Parallel Scavenge 、G1 用于新生代回收。
    • CMS、Serial OLD(MSC)、Parallel OLD、G1 用于老年代回收。
    • 连线表示可以配合使用
      下面介绍每个收集器:


      image.png

    细心的同学会发现这里没有G1呢????因为我想单独说明G1:

    G1收集器(Garbage-First):是当今收集器技术发展的最前沿的成果之一,G1是一款面向服务器端应用的垃圾收集器。 使用G1收集器时,java堆的内存布局就与其他收集器有很大差别,它将真个java堆划分为多个大小相等的独立区域(Region),虽然还保留新生代与老年代的概念,但新生代与老年代不再试物理隔离的了,他们都是一部分Region(不需要连续)的集合。G1具备如下特点:

    • 并行与并发:G1能充分利用多CPU、多核环境下的硬件优势,使用多个CPU(CPU或者CPU核心)来缩短Stop-The-World停顿的时间,部分其他收集器原本需要停顿java线程执行的GC动作,G1收集器仍然可以通过并发的方式让java程序继续执行。
    • 分代收集:与其他收集器一样,分代概念在G1中依然得以保留。虽然G1可以不需要其他收集器配合就能够独立管理整个GC堆,但它能够采用不同的方式去处理新创建的对象和已经存活了一段时间、熬过多次GC的旧对象以获取更好的收集效果。
    • 空间整合:与CMS的“标记–清理”算法不同,G1从整体来看是基于“标记–整理”算法实现的收集器,从局部(两个Region之间)上来看是基于“复制”算法实现的,但无论如何,这两种算法都意味着G1运行期间不会产生内存空间碎片,收集后能提供规整的可用内存。这个特性有利于程序长时间运行,分配大对象时不会因为无法找到连续内存空间而提前出发下一次GC。
      可预测的停顿:这是G1相对于CMS的另一大优势,降低停顿时间是G1和CMS共同的关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒,这几乎已经是实时java(RTSJ)的垃圾收集器的特性了。
    • 初始标记(Initial Marking):标记GC Roots能够直接关联到的对象,并且修改TAMS的值,能在正确可用的Region中创建对象,这阶段需要停顿线程,而且耗时很短。
    • 并发标记(Concurrent Marking):从GC Roots开始堆中对象进行可达性分析,找出存活的对象,这个时间耗时比较长,但可与用户程序并行执行。
    • 最终标记(Final Marking):为了修正和正在并发标记期间因用户程序继续运行而导致标记产生变动的那一部分没有标记记录,虚拟机将这一段对象变法记录在线程Rememberred Set logs里面,最终标记阶段需要把Remembered Set logs 的数据合并到Remembered Set中,这阶段需要停顿线程,但是可并发执行。
    • 筛选回收(Live Data Counting and Evacuation):对各个Region的回收截止和成本进行排序,根据用户期望的GC停顿时间来制定回收计划,这阶段可以做到和用户程序一起并发执行,但是因为值回收一部分Region,时间是用户可控制的,而且停顿用户线程将大幅度提高手机效率。

    G1看上去很美好,解决了CMS的碎片问题。但是实际商业应用中似乎还没有被肯定,后续版本优化应该会越来越好吧。

    jdk1.7 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)
    jdk1.8 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)
    jdk1.9 默认垃圾收集器G1

    相关文章

      网友评论

          本文标题:关于GC

          本文链接:https://www.haomeiwen.com/subject/ngrtgqtx.html