美文网首页
垃圾收集器

垃圾收集器

作者: ACtong | 来源:发表于2020-06-06 09:52 被阅读0次

    一、CMS收集器

    CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。大部分应用在浏览器的B/S系统的服务端上

    收集步骤:
      1. 初始标记(CMS initial mark)
        仅仅只是标记GC Roots能够直接关联到的对象,速度快
      1. 并发标记(CMS concurrent mark)
        从GC Roots的直接关联对象开始边遍历整个对象图的过程。速度快,不需要停顿用户线程,与垃圾收集线程并发执行
      1. 重新标记(CMS remark)
        修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,停顿时间较长。
      1. 并发清除(CMS concurrent sweep)
        清理删除掉标记阶段判断已经死亡的对象,由于不需要移动存活对象,这阶段是可以与用户线程同时并发的。

    其中初始标记、重新标记仍然需要“Stop The World”

    缺点:

    1、CMS对处理器资源非常敏感。在并发阶段,虽然不会导致用户线程停顿,但却会占用一部分线程,导致应用程序变慢,降低吞吐量

    2、在CMS的并发标记和并发清除阶段,用户线程还在继续运行,程序在运行自然还会伴随有新的垃圾对象不断产生,但这一部分垃圾对象是出现在标记过程结束以后,这部分称为“浮动垃圾(Floating Garbage)”
    CMS收集器无法处理“浮动垃圾”,有可能出现“Con-current Mode Failure”失败而导致另一次完全的“Stop The World”的Full GC产生。

    3、由于CMS是基于“标记-清除”算法实现的收集器,这也就意味着收集结束时会有大量空间碎片产品,也将会给大对象分配带来很大麻烦,往往会出现老年代还有很多剩余空间,但就是无法找到足够大的连续空间来分配当前对象,不得不提前触发一次Full GC。

    二、Garbage First收集器(简称G1)

    它是一个面向局部收集的设计思路和基于Region的内存布局形式。是主要面向服务端应用的垃圾收集器。在JDK 8 update 40后,G1收集器才被Oracle官方称为“全功能的垃圾收集器(Fully-Featured Garbage Collector)”

    在G1收集器出现之前所有的其他收集器,包括CMS在内,垃圾收集的目标要么是整个新生代(Minor GC),要么是整个老年代(Major GC),要么就是整个Java堆(Full GC)。而G1则是面向堆内存任何部分组成回收集(Collection Set,简称CSet)进行回收,衡量标准不再是属于哪个分代,而是哪块内存中存放的垃圾数量最多,回收收益最大,这就是G1收集器的Mixed GC模式。

    G1是把连续的Java堆划分为多个大小相等的独立区域(Region),每个Region都可以根据需要,扮演新生代的Eden空间、Survivor空间或者老年代空间,收集器能够扮演不同角色的Region采用不同的策略去处理,这样无论新创建的对象还是已经存活了一段时间、熬过多次收集的旧对象都能获得很好的收集效果。

    Region中还有一类特殊的Humongous区域,专门用来存储大对象。G1中的新生代和老年代不再是固定的,他们都是一系列区域(不需要连续)的动态集。G1将Region作为单次回收的最小单元,每次收集到的内存空间都是Region大小的整数倍,这样有计划的避免在整个Java堆中进行区域的垃圾收集。G1收集器会跟踪各个Region里面的垃圾堆的“价值”大小,然后在后台维护一个优先级列表,优先回收收益最大的哪些Region,这也是“Garbage First”名字的由来。这样在有限的时间内尽可能高的收集效率。

    如何解决Region中存在跨Region引用对象?

    使用记忆集避免全堆作为GC Roots扫描。每个Region都有自己的记忆集,这些记忆集会记录下别的Region指向自己的指针,并标记这些指针分别在哪些卡页的范围之内。
    G1记忆集在存储结构上本质是一种哈希表,Key是别的Region的起始地址,Value是一个集合,里面存储的元素是卡表的索引号。它是一种双向卡表(相互指向)。
    G1收集器占用更高的内存负担,G1至少要耗费大约相当于Java堆容量的10%至20%的额外内存来维持收集器工作。

    在并发阶段如何保证收集线程与用户线程互不干扰地运行?

    CMS收集器采用增量更新算法实现,而G1收集器则是通过原始快照(SATB)算法来实现。
    垃圾收集对用户线程的影响还体现在回收过程中新创建对象的内存分配上。G1为每一个Region设计了两个名为TAMS(Top at Mark Start)的指针,把Region中的一部分空间划分出来用于并发回收过程中的新对象分配,并发回收时新分配的对象地址都必须要在两个指针位置以上。
    G1收集器则是默认被隐式标记过的,默认他们时存活的,不纳入回收范围。
    CMS中的“Concurrent Mode Failure”失败会导致Full GC类似。如果内存回收的速度赶不上内存分配的速度,G1收集器也要被迫冻结用户线程执行,导致“Full GC”而产生长时间的“Stop The World”

    怎样建立起可靠的停顿预测模型?

    用户通过-XX:MaxGCPauseMillis参数指定的停顿时间意味着垃圾收集发生之前的期望值。
    G1收集器怎么做到才能满足用户的期望值呢?
    G1收集器的停顿预测模型是以衰减均值(Decaying Average)为理论基础来实现的。
    在垃圾收集过程中,G1收集器会记录每个Region的回收耗时、每个Region记忆集里的脏卡数量等各个可测量的步骤花费的成本,并分析出平均值、标准偏差、置信度等统计信息。

    G1收集器运行过程
    • 初始标记(Initial Marking)
      仅仅是标记GC Roots能直接关联到的对象,并且修改TAMS指针的值,让下一阶段用户线程并发运行时,能够正确地在可用的Region中分配新对象。这个阶段需要停顿现场,但是耗时短,并且还是借用Minor GC的时候同步完成,所以G1收集器在这个阶段并没有额外的停顿。
    • 并发标记(Concurrent Marking)
      从GC Roots开始对堆中对象进行可达性分析,递归扫描整个堆中的对象图,找出要回收的对象,这个耗时较长,但可与用户程序并发执行。当对象图扫描完成以后,还要重新处理SATB记录下的并发时有引用变动的对象。
    • 最终标记(Final Marking)
      对用户线程做另一个短暂的暂停,用于处理并发阶段结束后仍遗留下来的最后那少量的SATB记录
    • 筛选回收(Live Data Counting and Evacuation)
      负责更新Region的统计数据,对各个Region的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,可以自由选择任意多个Region构成回收集,然后把决定回收的那一部分Region的存活对象复制到空Region 中,再清理掉整个旧Region的全部空间。这里的操作涉及存活对象的移动,是必须暂定用户线程,由多条收集器线程并行完成的。

    从上述描述可以看出,G1收集器除了并发标记外,其余阶段也是要完全暂停用户线程的。

    总的来说,在小内存应用上CMS表现大概率仍然优于G1,而在大内存应用上G1大多能发挥其优势

    相关文章

      网友评论

          本文标题:垃圾收集器

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