前面文章中,咱们介绍了 Java 虚拟机的内存结构,Java 虚拟机的垃圾回收机制,那么这篇文章咱们说说具体执行垃圾回收的垃圾回收器。html
总的来讲,Java 虚拟机的垃圾回收器能够分为四大类别:串行回收器、并行回收器、CMS 回收器、G1 回收器。java
串行回收器是指使用单线程进行垃圾回收的回收器。由于每次回收时只有一个线程,所以串行回收器在并发能力较弱的计算机上,其专一性和独占性的特色每每能让其有更好的性能表现。算法
串行回收器能够在新生代和老年代使用,根据做用于不一样的堆空间,分为新生代串行回收器和老年代串行回收器。多线程
新生代串行回收器并发
串行收集器是全部垃圾回收器中最古老的一种,也是 JDK 中最基本的垃圾回收器之一。jvm
在新生代串行回收器中使用的是复制算法。在串行回收器进行垃圾回收时,会触发 Stop-The-World 现象,即其余线程都须要暂停,等待垃圾回收完成。所以在某些状况下,其会形成较为糟糕的用户体验。性能
使用 -XX:+UseSerialGC
参数能够指定使用新生代串行收集器和老年代串行收集器。当虚拟机在 Client 模式下运行时,其默认使用该垃圾收集器。学习
老年代串行回收器spa
在老年代串行回收器中使用的是标记压缩算法。其与新生代串行收集器同样,只能串行、独占式地进行垃圾回收,所以也常常会有较长时间的 Stop-The-World 发生。线程
但老年代串行回收器的好处之一,就是其能够与多种新生代回收器配合使用。若要启用老年代串行回收器,能够尝试如下参数:
-XX:UseSerialGC
:新生代、老年代都使用串行回收器。-XX:UseParNewGC
:新生代使用 ParNew 回收器,老年代使用串行回收器。-XX:UseParallelGC
:新生代使用 ParallelGC 回收器,老年代使用串行回收器。并行回收器在串行回收器的基础上作了改进,其使用多线程进行垃圾回收。对于并行能力强的机器,能够有效缩短垃圾回收所使用的时间。
根据做用内存区域的不一样,并行回收器也有三个不一样的回收器:新生代 ParNew 回收器、新生代 ParallelGC 回收器、老年代 ParallelGC 回收器。
新生代 ParNew 回收器
新生代 ParNew 回收器工做在新生代,其只是简单地将串行回收器多线程化,其回收策略、算法以及参数和新生代串行回收器同样。
新生代 ParNew 回收器一样使用复制的垃圾回收算法,其垃圾收集过程当中一样会触发 Stop-The-World 现象。但由于其使用多线程进行垃圾回收,所以在并发能力强的 CPU 上,其产生的停顿时间要短于串行回收器。
但在单 CPU 或并能能力弱的系统中,并行回收器效果会由于线程切换的缘由,其实际表现反而不如串行回收器。
要开启新生代 ParNew 回收器,可使用如下参数:
-XX:+UseParNewGC
:新生代使用 ParNew 回收器,老年代使用串行回收器。-XX:UseConcMarkSweepGC
:新生代使用 ParNew 回收器,老年代使用 CMS。-XX:ParallelGCThreads
:指定 ParNew 回收器的工做线程数量。新生代 Parallel GC 回收器
新生代 Parallel GC 回收器与新生代 ParNew 回收器很是相似,其也是使用复制算法,都是多线程、独占式的收集器,也会致使 Stop-The-World。但其他 ParNew 回收器的一个重大不一样是:其很是注重系统的吞吐量。
之因此说新生代 Parallel GC 回收器很是注重系统吞吐量,是由于其有一个自适应 GC 调节策略。咱们可使用 -XX:+UseAdaptiveSizePolicy
参数打开这个策略,在这个模式下,新生代的大小、Eden 和 Survivor 的比例、晋升老年代的对象年龄等参数都会被自动调节,已达到堆大小、吞吐量、停顿时间的平衡点。
Parallel GC 回收器提供了两个重要参数用于控制系统的吞吐量。
-XX:MaxGCPauseMillis
:设置最大垃圾收集停顿时间。在 ParallelGC 工做时,其会自动调整响应参数,将停顿时间控制在设置范围内。为了达到目的,其可能会使用较小的堆,但这会致使 GC 较为频繁。-XX:GCTimeRatio
:设置吞吐量大小,其实一个 0 - 100 的整数。假设 GCTimeRatio 的值为 n,那么系统将不花费超过 1/(1+n) 的时间用于垃圾手机。好比 GCTimeRatio 值为 19,那么系统用于垃圾收集的时间不超过 1 /(1+19) = 5%。默认状况下,它的取值是 99,即不超过 1% 的时间用于垃圾收集。新生代 Parallel GC 回收器可使用如下参数启用:
-XX:+UseParallelGC
:新生代使用 Parallel 回收器,老年代使用串行回收器。-XX:+UseParallelOldGC
:新生代使用 ParallelGC 回收器,老年代使用 ParallelOldGC 回收器。老年代 ParallelOldGC 回收器
老年代 ParallelOldGC 回收器也是一种多线程并发的回收器,与新生代 ParallelGC 收集器同样,其也是注重吞吐量的收集器,只不过其是做用于老年代。
ParallelOldGC 回收器使用的是标记压缩算法,只有在 JDK 1.6 中才可使用。咱们可使用-XX:UseParallelOldGC
参数在新生代中使用 ParallelGC 收集器,在老年代中使用 ParallelOldGC 收集器。参数 -XX:ParallelGCThreads
也能够用于设置垃圾回收时的线程数量。
与 ParallelGC 和 ParallelOldGC 不一样,CMS 回收器主要关注系统停顿时间。CMS 回收器全称为 Concurrent Mark Sweep,意为标记清除算法,其是一个使用多线程并行回收的垃圾回收器。
工做步骤
CMS 的主要工做步骤有:初始标记、并发标记、预清理、从新标记、并发清除和并发充值。其中初始标记和从新标记是独占系统资源的,而其余阶段则能够和用户线程一块儿执行。
在整个 CMS 回收过程当中,默认状况下会有预清理的操做,咱们能够关闭开关 -XX:-CMSPrecleaningEnabled
不进行预清理。由于从新标记是独占 CPU 的,所以若是新生代 GC 发生以后,马上出发一次新生代 GC,那么停顿时间就会很长。为了不这种状况,预处理时会刻意等待一次新生代 GC 的发生,以后在进行预处理。
主要参数
启动 CMS 回收器刻意使用参数:-XX:+UseConcMarkSweepGC
,线程并发数量刻意经过 -XX:ConcGCThreads
或 -XX:ParallelCMSThreads
参数设定。
此外,咱们还能够设置 -XX:CMSInitiatingOccupancyFraction
来指定老年代空间使用阈值。当老年代空间使用率达到这个阈值时,会执行一次 CMS 回收,而不像其余回收器同样等到内存不够用的时候才进行 GC。
咱们以前说过标记清除算法的缺点是会产生内存碎片,所以 CMS 回收器会产生较多内存碎片。咱们可使用 XX:+UseCMSCompactAtFullCollection
参数让 CMS 在完成垃圾回收后,进行一次内存碎片整理。使用 -XX:CMSFullGCsBeforeCompaction
参数设置进行多少次 CMS 回收后,进行一次内存压缩。
此外,若是但愿使用 CMS 回收 Perm 区,那么则能够打开 -XX:+CMSClassUnloadingEnabled
开关。打开该开关后,若是条件容许,那么系统会使用 CMS 的机制回收 Perm 区 Class 数据。
G1 回收器是 JDK 1.7 中使用的全新垃圾回收器,从长期目标来看,其是为了取代 CMS 回收器。
G1 回收器拥有独特的垃圾回收策略,和以前全部垃圾回收器采用的垃圾回收策略不一样。从分代看,G1 依然属于分代垃圾回收器。但它最大的改变是使用了分区算法,从而使得 Eden 区、From 区、Survivor 区和老年代等各块内存没必要连续。
在 G1 回收器以前,全部的垃圾回收器其内存分配都是连续的一块内存,以下图所示。
而在 G1 回收器中,其将一大块的内存分为许多细小的区块,从而不要求内存是连续的。
从上图能够看到,每一个Region被标记了 E、S、O 和 H,说明每一个 Region 在运行时都充当了一种角色。全部标记为 E 的都是 Eden 区的内存,它们散落在内存的各个角落,并不要求内存连续。同理,Survivor 区、老年代(Old)也是如此。
从上图咱们还能够看到 H 是以往算法中没有的,它表明 Humongous。这表示这些 Region 存储的是巨型对象(humongous object,H-obj),当新建对象大小超过 Region 大小一半时,直接在新的一个或多个连续 Region 中分配,并标记为 H。
堆内存中一个 Region 的大小能够经过 -XX:G1HeapRegionSize
参数指定,大小区间只能是1M、2M、4M、8M、16M 和 32M,总之是2的幂次方。若是G1HeapRegionSize 为默认值,即把设置的最小堆内存按照2048份均分,最后获得一个合理的大小。
工做步骤
G1 收集器的收集过程主要有四个阶段:
新生代 GC 与其余垃圾收集器的相似,就是清空 Eden 区,将存活对象移动到 Survivor 区,部分年龄到了就移动到老年代。
并发标记周期则分为:初始标记、根区域扫描、并发标记、从新标记、独占清理、并发清理阶段。其中初始标记、从新标记、独占清理是独占式的,会引发停顿。而且初始标记会引起一次新生代 GC。在这个阶段,全部将要被回收的区域会被 G1 记录在一个称之为 Collection Set 的集合中。
混合回收阶段会首先针对 Collection Set 中的内存进行回收,由于这些垃圾比例较高。G1 回收器的名字 Garbage First 就是这个意思,垃圾优先处理的意思。在混合回收的时候,也会执行屡次新生代 GC 和 混合 GC,从而来进行内存的回收。
必要时进行 Full GC。当在回收阶段遇到内存不足时,G1 会中止垃圾回收并进行一次 Full GC,从而腾出更多空间进行垃圾回收。
相关参数
打开 G1 收集器,咱们可使用参数:`-XX:+UseG1GC。
设置目标最大停顿时间,可使用参数:-XX:MaxGCPauseMillis
。
设置 GC 工做线程数量,可使用参数:-XX:ParallelGCThreads
。
设置堆使用率触发并发标记周期的执行,可使用参数:-XX:InitiatingHeapOccupancyPercent
。
从一开始的串行回收器,到后来的并行回收器、CMS回收器,到最后的 G1 回收器,垃圾回收器不断改进,使得垃圾回收效率不断提高。特别是分区思想诞生后,对于垃圾回收停顿时间的控制更加细腻,可让应用有更完美的延时控制,从而呈现更好的用户体验。
若是只是看,其实没法真正学会知识的。为了帮助你们更好地学习,我建了一个虚拟机群,专门讨论学习 Java 虚拟机方面的内容,每周针对我所发文章进行讨论答疑。若是你有兴趣,关注「Java技术精选」公众号,经过右下角菜单「入群交流」加我好友,小助手会拉你入群。