☆自动内存管理之垃圾回收器中的经典垃圾回收器(serial,parnew, CMS,G1)

JVM-2

来源于:深入理解jvm第3版

经典的一些收集器

新生代的收集器

  • Serial收集器

    • 单一线程工作
    • Stop The World
    • 客户端模式默认收集器
    • 简单高效
    • 标记-复制算法
    • 对于内存资源受限的环境, 它是所有收集器里额外内存消耗最小的;
    • 已经基本不适用了
    • 客户端模式默认收集器
  • ParNew收集器 是Serial收集器的并发版本

    • 可以多线程并行GC
    • 对cpu的依赖性比较高
    • 复制算法
      *ParNew收集器在单核心处理器的环境中不会有比Serial收集器更好的效果
      *并行处理
      并行(Parallel) : 并行描述的是多条垃圾收集器线程之间的关系, 说明同一时间有多条这样的线程在协同工作, 通常默认此时用户线程是处于等待状态。
      并发(Concurrent) : 并发描述的是垃圾收集器线程与用户线程之间的关系, 说明同一时间垃圾收集器线程与用户线程都在运行。 由于用户线程并未被冻结, 所以程序仍然能响应服务请求, 但由于垃圾收集器线程占用了一部分系统资源, 此时应用程序的处理的吞吐量将受到一定影响。
  • Parallel Scavenge收集器

    • 关注的侧重点在吞吐量,也被称为吞吐量优先的处理器
      *吞吐量就是处理器用于运行用户代码的时间与处理器总消耗时间的比值 即 吞吐量 = 运行用户代码时间 / (运行用户代码时间+垃圾处理时间)
    • 自适应的调节策略
      *并行收集的多线程收集器
      *通过复制算法进行垃圾清理
      *虚拟机会根据当前系统的运行情况,收集性能监控信息, 动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量。
      *适合在后台运算而不需要太多交互的分析任务,过多的停顿可能会影响交互体验

用于老年代的收集器

  • Serial Old
    *Serial Old收集器 Serial的老年版本
    *单线程的
    *标记-整理算法
    *供客户端模式下的HotSpot虚拟机使用
    *如果在服务端模式下, 它也可能有两种用途:
    *一种是在JDK 5以及之前的版本中与Parallel Scavenge收集器搭配使用
    *另外一种就是作为CMS收集器发生失败时的后备预案, 在并发收集发生Concurrent Mode Failure时使用。

  • Parallel Old收集器

    • 标记整理算法
    • 注重吞吐量或者处理器资源比较稀缺的场合
    • 并行的多线程
  • ⭐CMS收集器

    • 最短回收停顿时间
    • 标记清除算法
    • 四个步骤
      • 初始标记
        Stop The World
        标记一下GC Roots能直接关联到的对象, 速度很快;
      • 并发标记
        从GC Roots的直接关联对象开始遍历整个对象图的过程, 这个过程耗时较长但是不需要停顿用户线程, 可以与垃圾收集线程一起并发运行
      • 重新标记 Stop The World
        因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录 ,这个阶段的停顿时间通常会比初始标记阶段稍长一些,但也远比并发标记阶段的时间短
      • 并发清除
        清理删除掉标记阶段判断的已经死亡的对象, 由于不需要移动存活对象, 所以这个阶段也是可以与用户线程同时并发的。
    • 并发收集,低停顿
    • 对处理器资源要求比较高
    • 无法处理浮动垃圾,有可能出现“Con-current Mode Failure”失败进而导致另一次完全“Stop The World”的Full GC的产生。
    • 大量的内存碎片,还需要配合标记-清除算法统一整理
  • ⭐Garbage First(G1)收集器

    • 主要面向服务端的,
      服务端, 延迟小的好 . 但当功能为计算数据时,等个30秒再算也没毛病,这时吞吐量优先更合适.客户端,一般要给人用的,自然是低延迟好
      *G1整体是标记-整理算法,局部是标记-复制算法, 意味着不会产生空间碎片

    • 可预测停顿时间垃圾收集器

    • 是Region的内存布局形式
      G1不再坚持固定大小以及固定数量的分代区域划分,而是把连续的Java堆划分为多个大小相等的独立区域(Region),每一个Region都可以根据需要,扮演新生代的Eden空间、Survivor空间,或者老年代空间。
      根据不同类型的Region,选择不同的策略去进行收集管理。

    • 也是分代理论,但是不再是划分为老年代,新生代了

    • 可以管理全堆内存

    • 四个步骤 世界停止的时间比较短

      • 初始标记
        *Stop The World,对用户线程做另一个短暂的暂停
        *标记一下GC Roots能直接关联到的对象,并且修改TAMS指针的值,让下一阶段用户线程并发运行时,能正确的在可用的Region中分配新对象。
        *这个过程需要停顿线程,但是实在新生代收集线程的时候同步完成的,所以实际上没有额外的停顿

      • 并发标记
        *从GC Root开始对堆中对象进行可达性分析, 递归扫描整个堆
        里的对象图, 找出要回收的对象, 这阶段耗时较长, 但可与用户程序并发执行。 当对象图扫描完成以后, 还要重新处理SATB记录下的在并发时有引用变动的对象。

      • 最终标记
        *Stop The World
        *用于处理并发阶段结束后仍遗留下来的最后那少量的SATB记录。

      • 筛选回收
        *Stop the world
        *负责更新Region的统计数据, 对各个Region的回收价值和成本进行排序, 根据用户所期望的停顿时间来制定回收计划, 可以*选择任意多个Region构成回收集
        然后把决定回收的那一部分Region的存活对象复制到空的Region中
        再清理掉整个旧Region的全部空间
        这里的操作涉及存活对象的移动, 是必须暂停用户线程, 由多条收集器线程并行完成的。
        *G1相比于PS/PSold
        最大的好处是停顿的时间可控,可预测
        *收益优先
        G1收集器的停顿预测模型是以衰减均值为理论基础来实现的,也就是说根据这个理论来判断哪个的收益更高。
        哪块内存中存放的垃圾数量最多, 回收收益最大 ,就去回收那一块
        *Humongous区域, 专门用来存储大对象,默认将其看为老年代
        如果大对象超过一个regions,那么会用多个Humogous区域与来存储

    *G1整体是标记-整理算法,局部是标记-复制算法, 意味着不会产生空间碎片
    ☆自动内存管理之垃圾回收器中的经典垃圾回收器(serial,parnew, CMS,G1)

    *G1和cms的相同和区别:
    *G1整体是标记-整理算法,局部是标记-复制算法, 意味着不会产生空间碎片
    *CMS是标记-清除算法,会产生空间碎片

低延迟垃圾收集器(了解即可)

  • Shenandoah 收集器
    *Shenandoah摒弃了在G1中耗费大量内存和计算资源去维护的记忆集, 改用名为“连接矩阵”(Connection Matrix的全局数据结构来记录跨Region的引用关系。
    *降低了处理跨代指针时的记忆集维护消耗,
    *运行过程
    *初始标记(Initial Marking) : 与G1一样, 首先标记与GC Roots直接关联的对象, 这个阶段仍是“Stop The World”的, 但停顿时间与堆大小无关, 只与GC Roots的数量相关。
    *并发标记(Concurrent Marking) : 与G1一样, 遍历对象图, 标记出全部可达的对象, 这个阶段是与用户线程一起并发的, 时间长短取决于堆中存活对象的数量以及对象图的结构复杂程度
    *最终标记(Final Marking) : 与G1一样, 处理剩余的SATB扫描, 并在这个阶段统计出回收价值最高的Region, 将这些Region构成一组回收集(Collection Set) 。 最终标记阶段也会有一小段短暂的停顿。
    *并发清理(Concurrent Cleanup) : 这个阶段用于清理那些整个区域内连一个存活对象都没有找到的Region(这类Region被称为Immediate Garbage Region) 。
    *并发回收(Concurrent Evacuation) : 并发回收阶段是Shenandoah与之前HotSpot中其他收集器的核心差异。 在这个阶段, Shenandoah要把回收集里面的存活对象先复制一份到其他未被使用的Region之中。 复制对象这件事情如果将用户线程冻结起来再做那是相当简单的, 但如果两者必须要同时并发进行的话, 就变得复杂起来了。 其困难点是在移动对象的同时, 用户线程仍然可能不停对被移动的对象进行读写访问, 移动对象是一次性的行为, 但移动之后整个内存中所有指向该对象的引用都还是旧对象的地址, 这是很难一瞬间全部改变过来的。 对于并发回收阶段遇到的这些困难, Shenandoah将会通过读屏障和被称为“*s Pointers”的转发指针来解决 。 并发回收阶段运行的时间长短取决于回收集的大小。
    *初始引用更新(Initial Update Reference) : 并发回收阶段复制对象结束后, 还需要把堆中所有指向旧对象的引用修正到复制后的新地址, 这个操作称为引用更新。 引用更新的初始化阶段实际上并未做什么具体的处理, 设立这个阶段只是为了建立一个线程集合点, 确保所有并发回收阶段中进行的收集器线程都已完成分配给它们的对象移动任务而已。 初始引用更新时间很短, 会产生一个非常短暂的停顿。
    *并发引用更新(Concurrent Update Reference) : 真正开始进行引用更新操作, 这个阶段是与用户线程一起并发的, 时间长短取决于内存中涉及的引用数量的多少。 并发引用更新与并发标记不同, 它不再需要沿着对象图来搜索, 只需要按照内存物理地址的顺序, 线性地搜索出引用类型, 把旧值改为新值即可。
    *最终引用更新(Final Update Reference) : 解决了堆中的引用更新后, 还要修正存在于GC Roots中的引用。 这个阶段是Shenandoah的最后一次停顿, 停顿时间只与GC Roots的数量相关。
    *并发清理(Concurrent Cleanup) : 经过并发回收和引用更新之后, 整个回收集中所有的Region已再无存活对象, 这些Region都变成Immediate Garbage Regions了, 最后再调用一次并发清理过程来回收这些Region的内存空间, 供以后新对象分配使用。

  • ZGC收集器

    • 染色指针技术

内存分配和回收策略

  • 对象优先在Eden去分配,当eden没有足够空间分配时,虚拟机将发起一次小GC(Mirror Gc)
  • 大对象直接进入老年代
  • 长期存活的对象进入老年代
    • 15岁晋升到老年代
    • 动态的对象年龄判定
      • Survivor空间中相同年龄所有对象大小总和大于Survivor空间一半,就把年龄大于等于该年龄的对象直接进入老年代
  • 空间分配担保
    • 自己补充流程图进行梳理
      *在发生Minor GC之前, 虚拟机必须先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,
      *如果这个条件成立, 那这一次Minor GC可以确保是安全的。
      *如果不成立, 则虚拟机会先查看-XX: HandlePromotionFailure参数的设置值是否允许担保失败(Handle Promotion Failure) ;
      *如果允许, 那会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,
      *如果大于, 将尝试进行一次Minor GC, 尽管这次Minor GC是有风险的;
      *如果小于, 或者-XX:HandlePromotionFailure设置不允许冒险, 那这时就要改为进行一次Full GC。

JVM的一些工具

JVM的调优

上一篇:JVM垃圾回收器


下一篇:Java 12 新特性概述