Java常用的垃圾回收器


Java常用的垃圾回收器组合

   

1. Serial GC 
  使用:-XX:+UseSerialGC

  Serial GC 是新生代的垃圾回收器,Serial 体现在其收集工作是单线程的,并且在垃圾收集过程中,其他线程阻塞,进入 Stop the World 状态。新生代使用 Serial 垃圾回收器,是基于复制算法的。

2. Parallel Scavenge

  使用:-XX:+UseParallelGC

  Parallel Scavenge 收集器是一个新生代的垃圾回收器,采用的是复制算法。关注的是程序到达一个可控制的吞吐量(CPU 用于运行用户代码的时间 / CPU总消耗时间)。吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间)。高吞吐量可以最高效率的利用 CPU 时间,尽快完成程序的运算任务。

  其中:

  Parallel Scavenge 收集器有个自适应调节参数。这个参数是:-XX:UseAdaptiveSizePolic。这是一个开关参数,当这个开关打开后,就不需要手动指定新生代的大小(-Xmn)、Eden 和 Survivor 区的比例(-XX:SurvivorRatio)、晋升老年代对象大小(-XX:PertenureSizeThreshold)等参数细节了,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量。

  可以直接设置暂停时间或者吞吐量等目标,JVM 会自动进行适应性调整。

  -XX:MaxGCPauseMillis=value

  -XX:GCTimeRatio=N                   // GC时间和用户时间比例 = 1 / (N+1)

3. ParNew 

  使用:-XX:+UseParNewGC

  ParNew 垃圾回收器是 Serial 收集器的多线程版本。采用的也是复制算法。包括 Serial 收集器可用的所有控制参数。也就是可并行的进行垃圾回收。

  可通过 -XX:ParallelGCThreads 参数来限制垃圾收集器的线程数。

4. Serial Old

  Serial Old 是 Serial 垃圾收集器的老年代版本,同样是个单线程的收集器,是基于 标记-整理算法

5. Parallel Old

  Parallel Old 是 Parallel Scavenge 的老年代版本,使用的是 多线程-标记整理算法。JDK1.6 才开始使用。Parallel Scavenge 是可以保证新生代的吞吐量优先,但是不能保证整体吞吐量。Parallel Old 是为了在老年代同样提供吞吐量优先的垃圾回收器。

6. CMS

  使用:-XX:+UseConcMarkSweepGC

  CMS 是基于标记清除算法,设计的目的是减少停顿时间。基于标记清除算法,会存在内存碎片化的问题。其处理流程:

  1)初始标记(CMS-initial-mark),会导致 stw;

  2)并发标记(CMS-concurrent-mark),与用户线程同时运行;

  3)重新标记(CMS-remark),会导致 stw;

  4)并发清除(CMS-concurrent-sweep),与用户线程同时运行;

  5)并发重置状态等待下次 CMS 的触发(CMS-concurrent-reset),与用户线程同时运行;

7. G1

  G1 本质上是一个分代垃圾回收器。G1 垃圾回收器相对于 CMS 垃圾回收器,有两个改进:

  1)基于标记-整理算法,不产生内存碎片

  2)可以准确的控制停顿时间,在不牺牲吞吐量的情况下实现低停顿的垃圾回收

  G1 为了避免全域的垃圾收集,把堆内存划分成大小固定的几个独立区域,并跟踪这些区域的回收进度。同时在后台维护一个优先列表,每次根据收集时间,优先回收垃圾最多的区域

  G1 引入了额外的概念,Region。G1 垃圾回收器把堆划分一个个大小相同的 Region。在 HotSpot 的实现中,整个堆被划分成 2048 左右个的 Region。每个 Region 的大小在 1-32M 之间,具体的大小取决于堆得大小。

  G1 垃圾回收器的分代也是建立在这些 Region 的基础上的。对于 Region 来说,他会有一个分代的类型,并且是唯一一个。即:每一个 Region,它要么是 yong 的,要么是 old 的。还有一类十分特殊的 Humongous。所谓的 Humongous,就是一个对象的大小超了某一阈值 —— HotSpot中是 Region 的 1/2 ,那么它会被标记为 Humongous。如果我们审视 HotSpot 的其余的垃圾回收器,可以发现,这种对象以前被称为大对象,会被直接分配老年代。而在 G1 回收器中,则是做了特殊的处理。

  G1 并不要求相同类型的 region 要相邻。换言之,就是 G1 回收器不要求他们连续。当然在逻辑上,分代依旧是连续的。

   每一个分配的 Region,都可以分成两个部分,已分配的和未分配的。他们之间的界限被称为 top。把一个对象分配到 Region 内,只需要简单的增加 top 的值。这个做法实际上就是 bump-the-pointer。过程如下:(移动 top 线)

   Region 是 G1 回收器一次回收的最小单元,即每一次回收都是回收 N个 Region。这个 N 是多少,主要受到 G1 回收效率和用户设置的软实时目标有关。每一次的回收,G1 会选择可能回收最多垃圾的 Region 进行回收。与此同时,G1 回收器会维护一个空间 Region 的链表。每次回收后的 Region 都会被加入这个链表。每一次都只有一个 Region 处于被分配的状态中,被称为 current region 。在多线程的情况下会带来并发问题。G1 回收器采用和 CMS 一样的 TLABs 的手段,即为每一个线程分配一个 buffer,线程分配内存就在这个 buffer 内分配,但是当线程耗尽了自己的 buffer 以后,需要重新申请新的 buffer。这个时候依然会带来并发的问题。G1 回收器采用的是 CAS 操作。

 注:以上为学习摘抄做的笔记。