垃圾收集器
如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。
下面是Hotspot 虚拟机包含的所有收集器:
Serial
!(Seral.jpg)
- 特点:是最基本、发展历史最悠久的收集器。这是一个单线程收集器。但它的“单线程”的意义并不仅仅说明它只会使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是它在进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束。
- 应用年代:新生代
- 采用算法:复制算法
- 应用:是虚拟机运行在Client模式下的默认新生代收集器。
- 优势:简单而高效(与其他收集器的单线程比),对于限定单个CPU的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程效率。
- 缺点:Stop the world!
ParNew
!(ParNew.jpg)
- 特点:ParNew收集器其实就是Serial收集器的多线程版本
- 应用年代:新生代
- 采用算法:复制算法
- 应用:CPU较多
- 优势:除了Serial收集器外,目前只有它能与CMS收集器配合工作。
- 缺点:在单CPU环境,表现甚至不如Serial
Parallel Scavenge
!(Para.jpg)
- 特点:Parallel Scavenge收集器的目标是达到一个可控制的吞吐量。(吞吐量 = 运行用户代码时间 + 垃圾收集时间)。他的关注点是尽可能地缩短垃圾收集时用户线程的停顿时间。还可以根据当前系统的运行情况收集性能监测信息,动态调整这些参数以提供最合适的停顿时间或者最大吞吐量。
- 应用年代:新生代
- 采用算法:复制算法
- 优势:同特点
Serial Old
- 特点:Serial 的老年版本
- 应用年代:老年代
- 采用算法:标记-整理
- 应用:与Parallel Scavenge收集器搭配使用;作为CMS收集器的后备预案,在并发收集发生Conurrent Mode Failure 使用。
- 优势:
Parallel Old
- 特点:Parallel Old是Parallel Scavenge收集器的老年代版本
- 应用年代:老年代
- 采用算法:标记-整理
- 应用:注重吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel
CMS(重点)
- 特点:是HotSpot虚拟机中第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程同时工作。他的关注点在于尽可能地缩短垃圾收集时用户线程的停顿时间。
- 应用年代:老年代
- 采用算法:标记-清除
- 应用场景:大部分集中在互联网站或者B/S系统的服务端上的 Java 应用
- 优势:停顿时间短
它的运作过程相对来说较为复杂,分为 4 个步骤
初始标记、并发标记、重新标记、并发清除
!(cms.jpg)
其中,初始标记,重新标记这两个步骤仍然需要“Stop The World”。初始标记仅仅只标记一下GC Roots能直接关联到的对象,速度很快。并发标记阶段就是进行GC Roots Tracing的过程。
重新标记阶段则是为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记几率,这个阶段的停顿时间一般会比初始标记阶段稍长,但远比并发标记时间短。
整个过程耗时最长的阶段是并发标记,并发清除过程,但这两个过程可以和用户线程一起工作。
缺点:
- CMS收集器对CPU资源非常敏感。在并发阶段,它虽然不会导致用户线程停顿,但是会因为占用了一部分线程(或者说CPU资源)而导致应用程序变慢,总吞吐量会降低。
- CMS收集器无法处理浮动垃圾,可能出现“Conurrent Mode Failure”失败而导致另一次 Full GC的产生。由于 CMS 并发清理阶段用户线程还在运行着,伴随程序运行自然就还会产生新的垃圾,这一部分垃圾出现在标记过程之后,CMS无法在档次收集中处理掉它们,只好留待下一次GC时再清理掉。这部分垃圾就称为“浮动垃圾”。因此CMS收集器不能像其他收集器那样等到老年代几乎完全被填满了再进行收集,需要预留一部分空间提供并发收集时程序运作使用。在JDK1.5的默认设置下,CMS 收集器当老年代使用了 68% 的空间后就会被激活。如果预留空间无法满足程序需要,就会出现一次“Concurrent Mode Failure”失败,这时虚拟机将启动后备预案Serial Old。
- CMS是一款基于“标记-清除”算法实现的收集器,所以会有大量空间碎片问题。
G1
G1(Garbage-First),它是一款面向服务端应用的垃圾收集器,在多 CPU 和大内存的场景下有很好的性能。HotSpot 开发团队赋予它的使命是未来可以替换掉 CMS 收集器。
堆被分为新生代和老年代,其它收集器进行收集的范围都是整个新生代或者老年代,而 G1 可以直接对新生代和老年代一起回收。
G1 把堆划分成多个大小相等的独立区域(Region),新生代和老年代不再物理隔离。
通过引入 Region 的概念,从而将原来的一整块内存空间划分成多个的小空间,使得每个小空间可以单独进行垃圾回收。这种划分方法带来了很大的灵活性,使得可预测的停顿时间模型成为可能。通过记录每个 Region 垃圾回收时间以及回收所获得的空间(这两个值是通过过去回收的经验获得),并维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的 Region。
每个 Region 都有一个 Remembered Set,用来记录该 Region 对象的引用对象所在的 Region。通过使用 Remembered Set,在做可达性分析的时候就可以避免全堆扫描。
如果不计算维护 Remembered Set 的操作,G1 收集器的运作大致可划分为以下几个步骤:
- 初始标记
- 并发标记
- 最终标记:为了修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录,虚拟机将这段时间对象变化记录在线程的 Remembered Set Logs 里面,最终标记阶段需要把 Remembered Set Logs 的数据合并到 Remembered Set 中。这阶段需要停顿线程,但是可并行执行。
- 筛选回收:首先对各个 Region 中的回收价值和成本进行排序,根据用户所期望的 GC 停顿时间来制定回收计划。此阶段其实也可以做到与用户程序一起并发执行,但是因为只回收一部分 Region,时间是用户可控制的,而且停顿用户线程将大幅度提高收集效率。
具备如下特点:
- 空间整合:整体来看是基于“标记 - 整理”算法实现的收集器,从局部(两个 Region 之间)上来看是基于“复制”算法实现的,这意味着运行期间不会产生内存空间碎片。
- 可预测的停顿:能让使用者明确指定在一个长度为 M 毫秒的时间片段内,消耗在 GC 上的时间不得超过 N 毫秒。