垃圾收集3: 垃圾回收器

若是说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。如今为止尚未最好的垃圾收集器出现,更加没有万能的垃圾收集器,咱们能作的就是根据具体应用场景选择适合本身的垃圾收集器。HotSpot虚拟机中的7个垃圾收集器以下所示:java

若是两个收集器存在连线,说明能够搭配使用。所处的区域表示属于新生代仍是老年代收集器。

  • 单线程与多线程:单线程指的是垃圾收集器只使用一个线程进行收集,而多线程使用多个线程;
  • 串行与并行:串行指的是垃圾收集器与用户程序交替执行,这意味着在执行垃圾收集的时候须要停顿用户程序;并行指的是垃圾收集器和用户程序同时执行。除了 CMS 和 G1 以外,其它垃圾收集器都是以串行的方式执行。

Serial 收集器

以串行的方式执行,是单线程的收集器,只会使用一个线程来进行垃圾收集工做。

新生代采用复制算法,老年代采用标记-整理算法。git

它的优势是简单高效,对于单个 CPU 环境来讲,因为没有线程交互的开销,所以拥有最高的单线程收集效率。github

它是Client模式下的默认新生代收集器,由于在该应用场景下,分配给虚拟机管理的内存通常来讲不会很大。Serial 收集器收集几十兆甚至一两百兆的新生代停顿时间能够控制在一百多毫秒之内,只要不是太频繁,这点停顿是能够接受的。算法

ParNew收集器

是Serial收集器的多线程版本。除了使用多线程进行垃圾收集外,其他行为(控制参数、收集算法、回收策略等等)和Serial收集器彻底同样。

新生代采用复制算法,老年代采用标记-整理算法。服务器

Server模式下的虚拟机首选新生代收集器,除了性能缘由外,主要是由于除了 Serial 收集器,只有它能与 CMS 收集器配合工做。多线程

在单CPU的环境中不会有比Serial收集器更好的效果。可是随着使用的CPU的数量的增长,对于GC时系统资源的有效利用是有好处的。默认开启的线程数量与 CPU 数量相同,可使用 -XX:ParallelGCThreads 参数来设置线程数。闭包

并行:多条垃圾收集线程并行工做,单此时用户线程仍然处于等待状态。并发

并发:用户线程与垃圾收集线程同时执行,不必定是并行的。用户程序在继续运行,GC程序运行在另一个CPU上。jvm

Parallel Scavenge收集器

相似于ParNew收集器,是一个并行的多线程收集器、新生代收集器,使用复制算法。性能

其它收集器关注点是尽量缩短垃圾收集时用户线程的停顿时间,而它的目标是达到一个可控制的吞吐量,它被称为“吞吐量优先”收集器。这里的吞吐量指 CPU 用于运行用户代码的时间占总时间的比值。

停顿时间越短就越适合须要与用户交互的程序,良好的响应速度能提高用户体验。而高吞吐量则能够高效率地利用 CPU 时间,尽快完成程序的运算任务,适合在后台运算而不须要太多交互的任务。

缩短停顿时间是以牺牲吞吐量和新生代空间来换取的:新生代空间变小,垃圾回收变得频繁,致使吞吐量降低。

Serial Old收集器

Serial收集器的老年代版本,它一样是一个单线程收集器。它主要有两大用途:一种用途是在JDK1.5以及之前的版本中与Parallel Scavenge收集器搭配使用,另外一种用途是做为CMS收集器的后备方案,在并发收集发生 Concurrent Mode Failure 时使用。

Parallel Old收集器

是 Parallel Scavenge 收集器的老年代版本,使用多线程和“标记-整理”算法。

在注重吞吐量以及 CPU 资源敏感的场合,均可以优先考虑 Parallel Scavenge 加 Parallel Old 收集器。

CMS收集器

CMS(Concurrent Mark Sweep),Mark Sweep指的是标记-清除算法。

是一种以获取最短回收停顿时间为目标的收集器。于是很是符合在注重用户体验的应用上使用。

是HotSpot虚拟机第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程(基本上)同时工做。

分为如下四个流程:

  1. 初始标记:仅仅只是标记如下GC Roots能直接关联到的对象。速度很快,须要停顿。
  2. 并发标记:进行GC Root Tracing的过程,同时开启GC和用户线程,用一个闭包结构去记录可达对象。但在这个阶段结束,这个闭包结构并不能保证包含当前全部的可达对象。由于用户线程可能会不断的更新引用域,因此GC线程没法保证可达性分析的实时性。因此这个算法里会跟踪记录这些发生引用更新的地方。在整个回收过程当中耗时最长,不须要停顿。
  3. 从新标记:为了修正并发标记期间因用户程序继续运做而致使标记产生变更的那一部分对象的标记记录,这个阶段的停顿时间通常会比初始标记阶段稍长,但远比并发标记的时间段。
  4. 并发清除: 开启用户线程,同时GC线程开始对为标记的区域作清扫。

主要优势:并发收集、低停顿

具备如下三个缺点:

  • 对CPU资源敏感,吞吐量低。并发阶段虽然不会致使用户线程停顿,可是会由于占用了一部分CPU资源致使引用程序变慢,总吞吐量下降。
  • 没法处理浮动垃圾,可能出现Concurrent Mode Failure。浮动垃圾是指并发清除阶段因为用户线程继续运行而产生的垃圾,这部分垃圾只能到下一次 GC 时才能进行回收。因为浮动垃圾的存在,所以须要预留出一部份内存,意味着 CMS 收集不能像其它收集器那样等待老年代快满的时候再回收。若是预留的内存不够存放浮动垃圾,就会出现 Concurrent Mode Failure,这时虚拟机将临时启用 Serial Old 来替代 CMS。
  • 使用“标记-清除”算法会致使收集结束时有大量空间碎片产生。每每出现老年代空间剩余,但没法找到足够大连续空间来分配当前对象,不得不提早触发一次 Full GC。

G1收集器

G1 (Garbage-First)是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器,在多 CPU 和大内存的场景下有很好的性能,以极高几率知足GC停顿时间要求的同时,还具有高吞吐量性能特征,被视为JDK1.7中HotSpot虚拟机的一个重要进化特征。HotSpot 开发团队赋予它的使命是将来能够替换掉 CMS 收集器。

具备如下特色:

  • 并行与并发:G1能充分利用CPU、多核环境下的硬件优点,使用多个CPU(CPU或者CPU核心)来缩短Stop-The-World停顿时间。部分其余收集器本来须要停顿Java线程执行的GC动做,G1收集器仍然能够经过并发的方式让java程序继续执行。
  • 分代收集:虽然G1能够不须要其余收集器配合就能独立管理整个GC堆,可是仍是保留了分代的概念。
  • 空间整合:与CMS的“标记--清理”算法不一样,G1从总体来看是基于“标记整理”算法实现的收集器;从局部上来看是基于“复制”算法实现的,这意味着运行期间不会产生内存空间碎片。
  • 可预测的停顿:这是G1相对于CMS的另外一个大优点,下降停顿时间是G1 和 CMS 共同的关注点,但G1 除了追求低停顿外,还能创建可预测的停顿时间模型,能让使用者明确指定在一个长度为 M 毫秒的时间片断内,消耗在 GC 上的时间不得超过 N 毫秒。

其它收集器进行收集的范围都是整个新生代或者老年代,而 G1 能够直接对新生代和老年代一块儿回收。

G1 把堆划分红多个大小相等的独立区域(Region),新生代和老年代再也不物理隔离。

经过引入 Region 的概念,从而将原来的一整块内存空间划分红多个的小空间,使得每一个小空间能够单独进行垃圾回收。这种划分方法带来了很大的灵活性,使得可预测的停顿时间模型成为可能。经过记录每一个Region垃圾回收时间以及回收所得到的空间(这两个值是经过过去回收的经验得到),并维护一个优先列表,每次根据容许的收集时间,优先回收价值最大的 Region。

每一个 Region 都有一个 Remembered Set,用来记录该 Region 对象的引用对象所在的 Region。经过使用 Remembered Set,在作可达性分析的时候就能够避免全堆扫描。

若是不计算维护 Remembered Set 的操做,G1 收集器的运做大体可划分为如下几个步骤:

  • 初始标记:标记GC Roots能直接关联的对象,须要停顿,时间短
  • 并发标记:从GC Root开始对堆中对象进行可达性分析,耗时长
  • 最终标记:为了修正在并发标记期间因用户程序继续运做而致使标记产生变更的那一部分标记记录,虚拟机将这段时间对象变化记录在线程的 Remembered Set Logs 里面,最终标记阶段须要把 Remembered Set Logs 的数据合并到 Remembered Set 中。这阶段须要停顿线程,可是可并行执行。
  • 筛选回收:首先对各个 Region 中的回收价值和成本进行排序,根据用户所指望的 GC 停顿时间来制定回收计划。此阶段其实也能够作到与用户程序一块儿并发执行,可是由于只回收一部分 Region,时间是用户可控制的,并且停顿用户线程将大幅度提升收集效率。

参考资料

相关文章
相关标签/搜索