yaochow@home:~$

【介绍】Java垃圾回收机制

GC Roots Tracing

如何判断实例是否还应该被GC呢?

  1. 引用计数法: 给对象添加一个引用计数器,当有一个地方引用它就在计数器上+1,当一个引用失效计数器-1。当计数器为0是我们认为此对象实例不再被使用,可以被回收。但是此方法有一个问题,无法解决相互引用问题。

  2. GC Roots可达性算法: 通过可以做为”GC Roots”的对象作为根结点,向下搜索,没有引用链相连的对象我们认为可以被回收。 ​
    GC Roots包括:

    • 栈中引用的对象
    • 方法区中静态常量引用的对象
    • 方法区中静态属性引用的对象
    • Native方法中引用的对象

GC 常用算法

  1. 标记-清除算法:

    分为两个阶段,第一阶段标记出所有存活的对象,第二阶段堆未被标记的对象进行回收。标记-清除算法的缺点是会造成产生许多不连续的空间。 ​

  2. 复制算法:

    将回收区域分为相同的两块,每次使用一块。将存活的对象复制到另一块区域,然后将使用过的区域一次性清理掉。复制算法的缺点是将内存空间缩小到一半。

  3. 标记-整理算法:

    标记-整理算法是在标记-清除算法上做了改进,第一阶段也是标记出所有存活的对象,第二阶段是将所有存活的对象都移动到一侧,然后再清除掉边界以外的所有对象。标记-整理算法的缺点是在整理阶段要花费一些时间。

  4. 分代收集算法:

    根据内存中对象的存活周期不同,将内存分为几块使用合适的算法。

  5. 火车算法:

    火车算法把成熟对象空间划分为固定长度的内存块,算法每次在一个块中单独执行。每个块属于一个集合。

GC 收集器

  1. Serial收集器:

    Serial(串行)收集器是最古老的收集器,它的缺点是当Serial收集器想进行垃圾回收的时候,必须暂停用户的所有进程,即”Stop The World”,采用的是复制算法

  2. Serial Old收集器:

    Serial收集器的老年代版本,采用的是标记-整理算法。

  3. ParNew收集器:

    ParNew收集器是Serial收集器的多线程实现,在GC的时候依然会”Stop The World”。

  4. Parallel Scavenge收集器:

    Parallel Scavenge收集器关注的是吞吐量(高效利用CPU)。采用的是复制算法。

  5. Parallel Old收集器:

    Parallel Scavenge收集器的老年代实现。使用的是标记-整理算法。

  6. CMS收集器:

    CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。

    • 步骤:
      1. 初始标记:仅标记一下GC Roots能直接关联到的对象;速度很快;但需要”Stop The World”。
      2. 并发标记:进行GC Roots Tracing,标记出存活对象,应用程序也在运行;并不能保证可以标记出所有存活对象。
      3. 重新标记:为了修正并发标记期间因用户程序继续运作而导致标记变动的那一部分对象。需要”Stop The World”,且停顿时间比初始标记稍长,但远比并发标记短;采用多线程并行来提升效率。
      4. 并发清除:回收所有的垃圾对象。
    • 缺点:
      1. 对CPU资源非常敏感:虽然不会暂停用户线程,但因为占用一部分CPU资源,还是会导致应用程序变慢。
      2. 无法处理浮动垃圾(并发清除是产生的垃圾),可能出现”Concurrent Mode Failure(CMS预留内存空间无法满足程序需要)”失败。这时JVM启用后备预案:临时启用Serial Old收集器,而导致另一次Full GC。
      3. 产生大量内存碎片:使用标记-清除算法会产生不连续内存,无法找到足够连续空间会提前触发Full GC。
  7. G1:

    G1(Garbage-First)是JDK7-u4才推出商用的收集器。

    • 特点:
      1. 并行与并发:充分利用CPU、多核环境下的硬件优势;可以并行来缩短”Stop The World”停顿时间;也可以并发让垃圾收集与用户程序同时进行。
      2. 分代收集,收集范围包括新生代和老年代:能独立管理整个GC堆,而不需要与其他收集器搭配;能够采用不同方式处理不同时期的对象;虽然保留分代概念,但Java堆的内存布局有很大差别;将整个堆划分为多个大小相等的独立区域;
      3. 结合多种垃圾收集算法,空间整合,不产生碎片:从整体上看,是基于标记-整理算法;从局部看(两个Region间),是基于复制算法;是一种类似火车算法的实现;都不会产生碎片,有利于长时间运行;
      4. 可预测的停顿:低停顿的同时实现高吞吐量。
    • G1收集器运作过程:
      1. 初始标记:仅标记一下GC Roots能直接关联到的对象;
      2. 并发标记:进行GC Roots Tracing的过程;在刚才产生的集合中标记出存活对象;
      3. 最终标记:为了修正并发标记期间因用户程序继续运作而导致标记变动的那一部分对象。
      4. 筛选回收:首先排序各个Region的回收价值和成本;然后根据用户期望的GC停顿时间来指定回收计划。最后按计划回收一些价值高的Region中垃圾对象;
  8. ZGC:

    1. 所有阶段几乎都是并发执行的,还有三个非常短暂的STW阶段,所以ZGC并不是Zero Pause GC。
    2. 并发执行的保证机制,就是Colored Pointer和Load Barrier。Colored Pointer从64位的指针中,借了几位出来表示Finalizable、Remapped、Marked1、Marked0。所以不支持32位指针,也不支持压缩指针,且堆的上限是4TB。
    3. 像G1一样划分Region,但更加灵活:ZGC将堆划分为Region作为清理,移动,以及并行GC线程工作分配的单位。
    4. 和G1一样会做Compacting-压缩
    5. Remap-修正指针:最后将指针都妥帖的更新指向新地址。
    6. 没有G1占内存的Remember Set,没有Write Barrier的开销
    7. 支持Numa架构
    8. 并行
    9. 单代