上一篇文章中学习了 JVM
的垃圾回收机制,和内存分配和回收策略。不过这都是一些理论知识,这篇文章中会学习一下 HotSpot
虚拟机中的垃圾收集器,这都是垃圾回收理论的具体实现。
HotSpot
虚拟机中有多种收集器,不同的收集器特点也不同,各年代使用的收集器也可以根据应用的特点和要求进行组合。
Serial
收集器是一个单线程的收集器,它不仅只会使用一个 CPU
或一条收集线程去完成垃圾收集工作,而且在垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束。
Serial
收集器是 HotSpot
虚拟机在运行 Client
模式下的默认新生代收集器。在垃圾收集时,年轻代使用“复制”算法,老年代使用“标记-整理”算法。
但它也有优点,与其他收集器的单线程相比,由于没有现成交互的开销,专心做垃圾收集,所以其简单而高效;
可以使用 -XX:UseSerialGC
参数选择使用 Serial
收集器,此时年轻代采用 Serail
,老年代采用 Serial Old
。
ParNew
收集器是 Serial
收集器的多线程版本,除了使用多线程进行垃圾回收外,其他几乎一样。
它是许多运行在 Server
模式下的虚拟机首选的新生代收集器,一个很重要的原因是除了 Serial
收集器外,只有 ParNew
收集器与 CMS
收集器配合工作。垃圾收集时,年轻代使用“复制”算法,老年代使用“标记-整理”算法。
ParNew
收集器默认开启的线程数与 CPU
的数量相同,可以使用 -XX:ParallelGCThreas
参数来限制垃圾收集的线程数。使用 -XX:UseParNewGC
参数来使用 ParNew
收集器。
Parallel Scavenge
收集器是新生代收集器,也是使用复制算法的多线程收集器。与其他收集器不同的是,它关注的是达到一个可控制的吞吐量,吞吐量 = 运行代码时间 / (运行代码时间 + 垃圾收集时间)。
Parallel Scanenge
收集器提供了两个参数用于精确控制吞吐量。第一个是控制最大垃圾收停顿时间的 -XX:MaxGCPauseMillis
参数。它允许是一个大于 0
的毫秒数,收集器将尽可能保证内存回收时间不超过设定值。这个参数也不是越小越好,GC 停顿时间缩短是以牺牲吞吐量和新生代空间为代价换取的。
第二个是直接设置吞吐量大小的 -XX:GCTimeRatio
参数。它允许是一个大于 0
且小于 100
的整数,就是垃圾收集时间占总时间的比率。
另外, Parallel Scavenge
收集器拥有自适应调节机制,它不需要手工指定新生代的大小( -Xmn
)、 Eden
与 Survivor
区的比例( -XX:SurvivorRatio
)、晋升老年代对象大小 -XX:PretenureSizeThreshold
等细节参数,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整参数以提供最合适的停顿时间及最大的吞吐量。可使用 -XX:UseAdaptiveSizePolicy
参数来开启。
Serial Old
是 Serial
收集器的老年代版本,是一个单线程收集器,使用“标记-整理算法”。
Parallel Old
是 Parallel Scavenge
收集器的老年代版本,使用多线程和“标记-整理”算法。如果新生代选择了 Parallel Scavenge
收集器,老年代只能选择 Serial Old
收集器。
CMS(Concurrent Mark Sweep)
是一种以获取最短停顿时间为目标的收集器,使用“标记-清除”算法。如果应用重视响应速度,希望停顿时间最短,就可以选择 CMS
收集器。
它的运作过程可分为 4
个步骤:
GC Roots
能直接关联到的对象,速度很快,需要 Stop the world
。 GC Roots Tracing
过程。 CMS
的主要优点是并发收集、低停顿。但也有三个缺点:
CPU
资源非常敏感。并发阶段虽不会导致用户线程停顿,但会因为占用资源而导致程序变慢,总吞吐量降低。 Concurrent Mode Failure
失败而导致另一次 Full FC
的产生。 CMS
基于标记-清除算法,收集结束时会产生大量空间碎片。碎片过多时,无法找到足够的连续空间来分配大对象,不得不提前出发一次 Full GC
。 可以使用 -XX:UseConcMarkSweepGC
参数来选择 CMS
收集器。
G1(Garbage-First)
是一款面向服务端应用的垃圾收集器。它的特点如下:
CPU
、多核环境,使用多个 CPU
来缩短停顿的时间,部分需要其他收集器原本需要停顿 Java
线程执行的 GC
动作, G1
收集器可通过并发的方式让 Java
程序继续执行。 G1
收集器能独立管理整个 GC
堆,并且能采用不同的方式处理不同时期的对象。 G1
收集器从整体来看,基于“标记-整理”算法实现;从局部来看,基于“复制”算法实现。 G1
能明确指定垃圾收集的限制时间。 使用 G1
收集器时,将 Java
堆划分为多个大小相等的区域 Region
。 G1
跟踪各个 Region
的回收价值和成本(回收获得空间及回收时间),后台会维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的 Region
。它通过使用 Remembered Set
来避免全堆扫描。
G1
收集器的运行步骤可分为:
GC Roots
直接能关联到的对象,需要停顿,但耗时很短。 GC Roots
开始对堆中对象进行可达性分析,找出存活的对象,耗时较长,但可并发执行。 Region
的回收价值和成本进行排序,根据指定的 GC
停顿时间制定回收计划。 HotSpot
虚拟机中包含了七种垃圾收集器,如下图:
它们的组合说明如下:
新生代收集器 | 年老代收集器 | 说明 |
---|---|---|
Serial | Serial Old | 都是单线程,GC 时会暂停所有应用线程。 使用 -XX:+UseSerialGC 选项来开启 |
Serial | CMS + Serial Old | CMS 是并发 GC,不需要暂停所有应用线程。 当 CMS 进行 GC 失败时,会自动使用 Serial Old 策略进行 GC 使用 -XX:+UseConcMarkSweepGC 选项来开启 |
ParNew | CMS | ParNew 是 Serial 的并行版本,可以指定 GC 线程数 默认 GC 线程数为 CPU 的数量 |
ParNew | Serial Old | 使用 -XX:+UseParNewGC 选项来开启 |
Parallel Scavenge | Serial Old | Parallel Scavenge 策略关注吞吐量,适用于后台持久运行的应用程序 使用 -XX:+UseParallelGC 选项来开启 |
Parallel Scavenge | Parallel Old | Parallel Old 是 Serial Old 的并行版本 使用 -XX:+UseParallelOldGC 选项来开启 |
G1GC | G1GC | -XX:+UseG1GC #开启 -XX:MaxGCPauseMillis #暂停时间目标 |