JAVA垃圾收集器科普

/ JAVA / 0 条评论 / 442浏览 / 自动同步于GITHUB

Serial收集器(新生代)

垃圾收集时,必须暂停其他所有工作线程(Stop The World),采用的是复制回收算法,单线程收集

Serial Old搜集器(老年代)

Serial收集器的老年代版本

ParNew收集器(新生代)

serial收集器的多线程版本,没有太多的提升

Parallel Scavenge收集器(新生代)

复制回收算法,并发多线程收集器,特点是与其他关注缩短垃圾收集时停顿时间的收集器的关注点不同,目标是达到可控的吞吐量(=运行用户代码时间/(运行用户代码时间 + 垃圾收集时间)),参数的两种设置方式:

  1. 使用-XX:MaxGCPauseMillis参数,直接制定大于0的毫秒数,收集器将尽可能地保证手机时间不超过设定值。但如果值设定过小,可能导致频繁垃圾回收。
  2. 使用-XX:GCTimeRatio参数,设定值大于0小于100整数,如果设定49,则GC时间占总时间的2%(1 / (1 + 49)),默认值为99.

另外还有一个常用参数-XX:+UseAdaptiveSizePolicy,是开关参数,设定后无需手工指定新生代、Eden区、Survivor区的比例、晋升老年代年龄(-XX:PretenureSizeThreshold),虚拟机将自动调整这些参数以提供最适合的停顿时间和吞吐量。

Parallel Old收集器(老年代)

Parallel Scavenge收集器的老年代版本

CMS收集器(老年代)

CMS(Concurrent Mark Sweep)收集器,是有较大提升的一个版本,采用的是标记清清理算法,同时是地一个真正意义上的并发收集器,以获取最短回收停顿时间为目标。

垃圾收集过程分为4个步骤:

  1. 初始标记
  2. 并发标记
  3. 重新标记
  4. 并发清除

初始标记、重新标记两个步骤仍旧需要"Stop The World",但这两个阶段都是耗时比较短的阶段。初始标记,只是标记GC Roots能够直接到达的对象,速度很快;并发标记阶段为进行GC Tracing的过程;重新标记阶段为了修正并发标记期间用户程序继续运作而导致的标记变动部分记录,比初始标记阶段时间稍长,但远低于并发标记阶段。

不足点:

  1. 并发程序的通病,对CPU资源敏感,线程占用资源不可避免的导致程序变慢(吞吐量降低),及线程切换的消耗,默认启动的线程数量((CPU数量 + 3) / 4)。
  2. 标记清楚回收算法的常见问题,导致内存空间的碎片,碎片过多大对象分配会带来麻烦,无法找到足够大空间时,不得不提前触发Full GC。参数-XX:+UseCMSCompactAtFullCollection用于在CMS收集器顶不住要FullGC时开启碎片压缩整合(默认即是开启的),但整合过程无法并发。参数-XX:CMSFullGCsBeforeCompaction设置多少次不压缩后执行一次压缩整合(默认值0)
  3. 收集器运行时需要为浮动垃圾(并发搜集阶段用户线程仍在运行而产生的垃圾)预留足够内存空间,浮动垃圾在下一次GC时清理掉,因此不能像其他收集器一样等到几乎被填满时再进行收集。JDK1.5中默认68%空间时收集,JDK1.6中为92%,使用-XX:CMSInitiatingOccupancyFraction参数设置百分比。
  4. concurrent mark时预留的内存无法满足程序需要,会导致Concurrent Mode Failure失败,这是虚拟机启用后备预案,临时使用Serial Old收集器重新进行老年代的垃圾收集。
  5. CMS作为老年代收集器,无法与Parallel Scavenge收集器配合使用,只能与Serial收集器或ParNew收集器配合使用

G1收集器(不再物理性分代)

最新的收集器,目标替换CMS收集器,仍需要更多的检验。从2004年论文发布起,近10年时间才推出的G1收集器的商用版本,采用化整为零的思路,将Java堆分成多个Region进行管理。但由于对象的引用实现上并没有看起来这么简单,与新生代老年代类似,每个Region区均为此留出了Remembered Set来避免全堆扫描。

G1收集器运行大致分为4个步骤:

  1. 初始标记
  2. 并发标记
  3. 最终标记
  4. 筛选回收

4个阶段与CMS收集器相似,在最终标记阶段,将并发阶段写在线程Remembered Set Logs里的标记变动记录合并到Remembered Set中。筛选回收阶段对各个Region回收价值和成本先进行排序,根据用户期望GC停顿时间来制定回收计划。

G1收集器特点:

  1. 并行与并发,多CPU环境缩短Stop-The-World
  2. 将整个Java堆分成多个大小相等的独立的Region,分代概念仍在G1收集器保留,但新生代和老年代不再是物理隔离的了,它们都是一部分Region的集合
  3. 可以不需要其他收集器配合就能独立管理整个GC堆,并能够采用不同方式处理新创建的和旧的对象
  4. 整体上采用"标记-整理"算法,局部(两个Region之间)基于"复制回收"算法
  5. 一大特色优势点,可预测的停顿。与CMS收集器一样关注降低停顿时间之外,还建立了可预测的停顿时间模型,能让使用者明确指定控制在长度为M的毫秒时间片段内,消耗在垃圾收集上的时间不超过N毫秒。

可预测的停顿,建立在有计划地避免在整个Java堆中进行全区域垃圾收集,G1跟踪各个Region里的垃圾堆积的价值大小(回收所获得的空间大小以及回收所需时间的经验值),在后台维护一个优先级列表,每次根据允许的收集时间优先回收高价值Region (这也是Garbage-First名称的由来)。

Sun给出测试结果显示,G1收集器对停顿时间的控制比CMS要更加地稳定可靠得多。