Serial是一类用于新生代的单线程收集器,采用复制算法进行垃圾收集。Serial进行垃圾收集时,只用一条单线程执行垃圾收集工作,所用的用户必须暂停。
serial垃圾收集器执行过程
当应用程序进行到一个安全的节点的时候,所有的线程全都暂停,等到GC完成后,应用程序线程继续执行。
优势:
缺点:
适用场景:Client 模式(桌面应用);单核服务器。
-XX:+UserSerialGC #选择Serial作为新生代垃圾收集器
parNew收集器其实就是Serial的一个多线程版本,其在单核cpu上的表现并不会比Serail收集器更好,在多核机器上,其默认开启的收集线程数与cpu数量相等。-XX:ParallelGCThreads #设置JVM垃圾收集的线程数
当用户线程都执行到安全点时,所有线程暂停执行,采用复制算法进行垃圾收集工作,完成之后,用户线程继续开始执行。
优点:
缺点:
适用场景:ParNew是许多运行在Server模式下的虚拟机中首选的新生代收集器。因为CMS收集器只能与serial或者parNew联合使用,在当下多核系统环境下,首选的是parNew与CMS配合。ParNew收集器也是使用CMS收集器后默认的新生代收集器。也可以使用如下命令进行强制指定。
-XX:UseParNewGC #新生代采用ParNew收集器
Parallel Scavenge也是一款用于新生代的多线程收集器,也是采用复制算法。与ParNew的不同之处在于 Parallel Scavenge收集器的目的是达到一个可控制的吞吐量,而ParNew收集器关注点在于尽可能的缩短垃圾收集时用户线程的停顿时间。
所谓吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值, 即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)。
优点:
适用场景:注重吞吐量高效利用CPU,需要高效运算,且不需要太多交互。
-XX:MaxGCPauseMilis 控制最大垃圾收集停顿时间,参数值是一个大于0的毫秒数,收集器尽可能保证回收花费时间不超过设定值。但将这个值调小,并不一定会使系统垃圾回收速度更快,GC停顿时间是以牺牲吞吐量和新生代空间换来的。
-XX:GCTimeRadio 设置吞吐量大小,参数值是一个(0,100)两侧均为开区间的整数。也是垃圾收集时间占总时间的比率,相当于是吞吐量的倒数。若把参数设置为19,则允许的最大GC时间就占总时间的5%(1/(1+19))。默认值是99,即允许最大1%的垃圾收集时间。
-XX:+UserAdaptiveSizePolicy 当打开这个函数,就不需要手动指定新生代的大小,Eden与Survivor区的比例(-XX:SurvivorRatio,默认是8:1:1),晋升老年代的对象年龄(-XX:PretenureSizeThreshold)等参数。JVM会动态调整这些参数,以提供最合适的停顿时间或者最大的吞吐量,这种调节方式称为GC自适应的调节策略.
Serial Old是Serial收集器的老年代版本,同样是一个单线程收集器,使用标记-整理算法。下图是Serial收集器与Serial Old收集器的运行示意图。
适用场景:Client模式;单核服务器;与Parallel Scavenge收集器搭配;作为CMS收集器的后备方案,在并发收集发生Concurrent Mode Failure时使用
Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法,可以充分利用多核CPU的计算能力。下图是两种收集器合作的运行示意图
适用场景:注重吞吐量与CPU资源敏感的场合,与Parallel Scavenge 收集器搭配使用,jdk7和jdk8默认使用该收集器作为老年代收集器。使用参数进行指定
-XX:+UserParallelOldGC
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。采用的算法是“标记-清除”,运作过程分为四个步骤
其中初始标记和重新标记这两个步骤仍然需要"stop the world"。耗时最长的并发标记与并发清除过程收集器线程都可以与用户线程一起工作,总体上来说CMS收集器的内存回收过程是与用户线程一起并发执行的。
-XX:UserCMSCompactAtFullCollection #开启碎片整理(默认是开的)
-XX:CMSFullGCsBeforeCompaction #执行多少次不压缩的Full GC之后,跟着来一次压缩的Full GC
优点:
缺点:
适用场景:重视服务器响应速度,要求系统停顿时间最短。可以使用参数-XX:+UserConMarkSweepGC来选择CMS作为老年代回收器。
G1收集器是一款面向服务端应用的垃圾收集器,目前是JDK9的默认垃圾收集器。
并行与并发:G1能充分利用多CPU,多核环境下的硬件优势。
分代收集:能够采用不同的方式去处理新创建的对象和已经存活了一段时间的对象,不需要与其他收集器进行合作。
空间整合:G1从整体上来看基于“标记-整理”算法实现的收集器,从局部上看是基于复制算法实现的,因此G1运行期间不会产生空间碎片。
可预测的停顿:G1能建立可预测的时间停顿模型,能让使用者明确指定一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒。G1收集器将这个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但两者之间不是物理隔离的。他们都是一部分Region的集合。下图是Java堆的划分示意图。
每一个方块就是一个区域,每个区域可能是 Eden、Survivor、老年代,每种区域的数量也不一定。JVM 启动时会自动设置每个区域的大小(1M ~ 32M,必须是 2 的次幂),最多可以设置 2048 个区域(即支持的最大堆内存为 32M*2048 = 64G),假如设置 -Xmx8g -Xms8g,则每个区域大小为 8g/2048=4M。
G1收集器可以有计划地避免在整个Java堆全区域的垃圾收集。G1可以跟踪各个Region里面垃圾堆积的价值大小(回收所获得的空间大小及回收所需时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,收集加载最大的region,这种方式保证了有限时间内可以获取尽可能多高的收集效率。
为了在 GC Roots Tracing 的时候避免扫描全堆,在每个 Region 中,都有一个 Remembered Set 来实时记录该区域内的引用类型数据与其他区域数据的引用关系(在前面的几款分代收集中,新生代、老年代中也有一个 Remembered Set 来实时记录与其他区域的引用关系),在标记时直接参考这些引用关系就可以知道这些对象是否应该被清除,而不用扫描全堆的数据。
过程:
适用场景:要求尽可能可控 GC 停顿时间;内存占用较大的应用。可以用 -XX:+UseG1GC 使用 G1 收集器,jdk9 默认使用 G1 收集器
参数 | 描述 |
---|---|
UserSerialGC | 采用Serial+ Serial Old的收集器组合进行垃圾回收 |
UserParNewGC | 使用ParNew+Serial Old组合 |
UseConMarkSweepGC | 使用ParNew+CMS+SerialOld组合进行垃圾回收。SerialOld是在Concurrent Mode Failure失败后的后备收集器使用 |
UseParallelGC | 虚拟机运行在Server模式下的默认值。使用Parallel Scavenge +Serial Old的收集器组合进行垃圾回收 (jdk8用的就是这个,但是老年代用的是Parallel Old) |
UseParallelOldGC | 使用Parallel Scavenge + Parallel Old组合进行垃圾回收 |
SurvivorRatio | 新生代中Eden区与Survior区域的容量比值,默认为8,代表Eden:Survior=8:1 |
PretenureSizeThreshold | 直接晋升到老年代的对象的大小 |
MaxTenuringThreshold | 晋升到老年代对象的年龄,超过这个数值进入老年代 |
UseAdaptiveSizePolicy | 动态调整Java堆中各个区域的大小以及进入老年代的年龄 |
HandlePromotionFailure | 是否允许分配担保失败,即老年代的剩余空间不足以应付新生代的整个Eden和Survivor区的 所有对象存活的极端情况 |
ParallelGCThreads | 并行GC时进行内存回收的线程数 |
GCTimeRatio | GC时间占总时间的比率,默认值99%,即允许1%的GC时间,仅在使用Parallel Scavenge收集器时生效 |
MaxGCPauseMillis | GC的最大停顿时间,仅在使用Parallel Scavenge收集器时生效 |
CMSInitiatingOccupancyFraction | 设置CMS在老年代空间被使用多少后触发垃圾回收,默认是92%,仅在使用CMS收集器时生效 |
UseCMSCompactAtFullCollection | 设置CMS收集器在完成垃圾收集后是否要进行一次内存整理。仅在使用CMS收集器时生效 |
CMSFullGCsBeforeCompaction | 设置CMS收集器进行若干次垃圾收集后再启动一次内存碎片整理。仅在使用CMS时生效 |
PrintGCDetails | 查看程序运行时的GC细节 |