JVM垃圾收集—垃圾收集器及常见组合参数
Posted 向着百万年薪努力的小赵
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM垃圾收集—垃圾收集器及常见组合参数相关的知识,希望对你有一定的参考价值。
链接: JVM垃圾收集—垃圾收集算法
上一篇介绍了垃圾收集算法及分区,这篇我们来学习垃圾收集器
文章目录
- Serial
- ParNew
- Parallel Scavenge
- Serial Old
- Parallel Old
- CMS (Concurrent Mark Sweep)
- G1
- 理解吞吐量和停顿时间
- 如何选择合适的垃圾收集器呢
首先我们要知道垃圾收集器有三种类型:
串行收集器 Serial 和 Serial Old
只能有一个垃圾回收线程执行,用户线程暂停。(适用于内存较小的嵌入式设备)
并行收集器[吞吐量优先] Paraller Scanvenge、Parallel Old
多条垃圾收集线程并行工作,但此时用户线程仍然处于等待阶段。(适用于科学计算、后台处理等若干交互场景)
并发收集器[停顿时间优先] CMS、G1
用户线程和垃圾收集线程同时执行(但并不一定是并行的,可能是交替执行的),垃圾收集线程在执行的时候不会停顿用户线程的运行。(适用于相对时间有要求的场景,比如WEB)
我按照发展顺序给大家介绍一下:
Serial
- 复制算法
- 新生代
- 单线程收集器
特点:它只会使用一个CPU或者一条收集线程去完成垃圾收集工作,更重要的是其在垃圾收集的时候需要暂停其他线程。
优点:简单高效
缺点:收集过程需要暂停所有线程。
应用:Client模式下的默认新生代收集器(Serial收集器是最基本、发展历史最悠久的收集,之前(JDK1.3.1之前)是虚拟机新生代收集器的唯一选择。)
ParNew
- 复制算法
- 新生代
- 多线程收集器
特点:ParNew收集器实质上是Serial收集器的多线程并行版本,除了同时使用多条线程进行垃圾收集之外,其余的行为包括Serial收集器可用的所有控制参数(例如:-XX:SurvivorRatio、-XX: PretenureSizeThreshold、-XX:HandlePromotionFailure等)、收集算法、Stop The World、对象分配规 则、回收策略等都与Serial收集器完全一致,在实现上这两种收集器也共用了相当多的代码。
优点:多CPU时,比Serial效率更高。
缺点:收集过程暂停所有的应用程序,单CPU核心时比Serial效率差。
应用:运行在Server模式下的虚拟机中首选的新生代收集器。
Parallel Scavenge
此收集器与吞吐量关系密切,故也称为吞吐量优先收集器。
- 复制算法
- 新生代
- 多线程收集器
- 关注吞吐量
特点:多线程
Parallel Scavenge收集器使用两个参数控制吞吐量:
控制最大的垃圾收集停顿时间 XX:MaxGCPauseMillis
直接设置吞吐量的大小 XX:GCTimeRatio
吞吐量 = 运行用户代码时间 / 运行用户代码时间 + 运行垃圾收集时间。
如果虚拟机完成某个任务,用户代码加上垃圾收集器总共耗时100分钟,其中垃圾收集器花费了1分钟,那吞吐量就是 99 / 100= 99%。
吞吐量越大,意味着垃圾收集的时间更短、则用户代码可以充分利用CPU资源,尽快完成程序的运算任务。
这里不是你设置了就一定有效,虚拟机会尽可能的靠近你设置的数值,并不是绝对一致
Serial Old
Serial Old 是Serial 收集器的老年代版本
- 标记整理
- 老年代
- 单线程收集器
Parallel Old
- 标记整理
- 老年代
- 多线程收集器
- 关注吞吐量
特点:多线程,采用标记-整理算法。
应用场景:注重高吞吐量以及CPU资源敏感的场合
CMS (Concurrent Mark Sweep)
是一种以获取最短回收停顿时间为目标的收集器
- 标记清除
- 老年代
- 并发收集器
- 关注最短停顿时间
应用场景:适用于注重服务的响应速度,希望系统停顿时间最短,给用户带来良好的体验。比如web服务,b/s结构。
工作分为四步:
第一步、初始标记(STW),标记GC Roots能直接关联到的对象,速度非常快。
第二步、并发标记,进行GC Roots Tracing ,就是从GC Roots开始找到它能引用的所有对象的过程。
第三步、重新标记(STW),为了修成并发标记期间因用户程序继续运作导致标记产生变动的一部分对象的标记记录。这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间要短。
第四步、并发清除,在整个过程中耗时最长的并发标记和并发清除过程,收集器线程都可以与用户线程一起工作,因此,从总体上看,CMS收集器的内存回收过程与用户线程一起并发执行的
优点:并发收集、并发清除、低停顿。
缺点:对CPU要求高,无法处理浮动垃圾、产生大量空间碎片、并发阶段会降低吞吐量。
- 对CPU敏感,并发阶段虽然不会导致用户线程暂停,但是它总是要线程执行,还是会占用CPU资源,(一定程度上也是,吞吐量的下降)
- 无法处理浮动垃圾:在最后一步并发清理过程中,用户线程执行也会产生垃圾,但是这部分垃圾是在标记之后,所以只有等到下一次 gc 的时候清理掉。
- 产生大量空间碎片、并发阶段会降低吞吐量。
这里有一个刁钻的面试问题:
CMS默认晋升老年代为6的原因: 简单来说,CMS对内存尤其敏感,且会导致单线程Serial FullGC 这个是非常严重的后果,而从结果上说越大的MaxTenuringThreshold会更快的导致heap的碎片化(不光old 区,首先要明白对于内存的分配并不是真的一个对象一个对象紧密排列的),所以历代CMS 默认这个值都会比较小(JDK8以前是4,之后调整为6)
G1
- 分代收集(仍然保留分代的概念)
- 并行与并发
- 老年代和新生代
- 关注最短停顿时间
- 内存分为Region[ˈriːdʒən] 区(内存是否连续)
- 可以设置最短停顿时间
工作也是分为四步:
第一步、初始标记(STW),标记GC Roots能直接关联到的对象(速度很快)。
第二步、并发标记,进行 GC Roots Tracing,就是从GC Roots开始找到它能引用的所有其他对象的过程。
第三步、最终标记(STW),为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分的标记记录。这个阶段的停顿时间一般会比初始标记阶段稍微长,但是要比并发标记要短。
第四步、筛选回收(STW),对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间指定回收计划。
G1 总结:
JDK 7 开始使用,JDK8非常成熟,JDK9默认的垃圾收集器。
如果停顿时间过短,会造成频繁垃圾回收,会导致OOM:GC overhead limitexceeded (超过98%的时间用来做GC并且回收了不到2%的堆内存时会抛出此异常)
虚拟机怎么判断是否需要使用G1收集器?
答:50%以上的堆被存活对象占用、对象分配和晋升的速度变化非常大、垃圾回收时间较长。
理解吞吐量和停顿时间
停顿时间 = 垃圾收集器进行垃圾回收的执行时间
吞吐量 = 运行用户代码时间 / 运行用户代码时间 + 运行垃圾收集时间。
如果虚拟机完成某个任务,用户代码加上垃圾收集器总共耗时100分钟,其中垃圾收集器花费了1分钟,那吞吐量就是 99 / 100= 99%。
吞吐量越大,意味着垃圾收集的时间更短、则用户代码可以充分利用CPU资源,尽快完成程序的运算任务。
停顿时间越短就越适合需要和用户交互的程序,良好的响应速度能提升用户体验; 高吞吐量则可以高效地利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不 需要太多交互的任务。
吞吐量和停顿时间是衡量垃圾回收器的标准,我们进行调优也是观察这两个变量。
如何选择合适的垃圾收集器呢
这个准则只能参考,因为性能取决于堆的大小,应用程序维护的实时数据量以及可用处理器的数量和速度。
大白话:牛逼哄哄的硬件设备不怎么需要调优,垃圾设备才考验你的调优技能。
如果应用程序的内存在100M左右,使用串行收集器 -XX:+UseSerialGC。
如果是单核心,并且没有停顿要求,默认收集器,或者选择带有选项的-XX:+UseSerialGC
如果允许停顿时间超过1秒或者更长时间,默认收集器,或者选择并行-XX:+UseParallelGC
如果响应时间最重要,并且不能超过1秒,使用并发收集器 -XX:+UseConcMarkSweepGC or -XX:+UseG1GC
1.8默认的垃圾回收:PS + ParallelOld
- -XX:+UseSerialGC = Serial New (DefNew) + Serial Old
小型程序。默认情况下不会是这种选项,HotSpot会根据计算及配置和JDK版本自动选择收集器 - -XX:+UseParNewGC = ParNew + SerialOld
这个组合已经很少用(在某些版本中已经废弃)
为什么废弃的官方解释:
链接: Why Remove support for ParNew+SerialOld and DefNew+CMS in the future? - -XX:+UseConc(urrent)MarkSweepGC = ParNew + CMS + Serial Old
- -XX:+UseParallelGC = Parallel Scavenge + Parallel Old (1.8默认) 【PS +SerialOld】
- -XX:+UseParallelOldGC = Parallel Scavenge + Parallel Old
- XX:+UseG1GC = G1
JVM垃圾收集器组合--各种组合对应的虚拟机参数实践
前言
相信很多人都看过下面这张图,(来自《深入理解Java虚拟机:JVM高级特性与最佳实践》)
在学完几种垃圾收集器类型及组合后,打算看看实际中程序用到的垃圾收集器。
但是在jconsole中看到的,如下:
两边的名称并不完全匹配,给我造成了很多困惑之处。
实际上,jconsole里面用到的,我想应该是JVM内部使用到的名字。
本文主要讲解新生代、老年代可用的垃圾收集器,如何查看运行中程序使用的垃圾收集器,可用的垃圾收集器组合及对应参数配置等。
资料来源于:
1、《深入理解Java虚拟机:JVM高级特性与最佳实践》
2、http://www.fasterj.com/articles/oraclecollectors1.shtml
一、垃圾收集器
1.新生代
1.1 Copy(别名:Serial、DefNew)
-the serial copy collector, uses one thread to copy surviving objects from Eden to Survivor spaces and between Survivor spaces until it decides they‘ve been there long enough, at which point it copies them into the old generation.
译文:Serial垃圾收集器,使用单线程完成下列工作:
从Eden区和From Survivor区拷贝存活对象到To Survivor区域、当存活对象在Survivor区超过年龄阈值时,拷贝到老年代。
这里的名称为Copy,实际上就是上图的Serial收集器。
1.2 PS Scavenge(别名:Parallel Scavenge、PSYoungGen)
-the parallel scavenge collector, like the Copy collector, but uses multiple threads in parallel and has some knowledge of how the old generation is collected (essentially written to work with the serial and PS old gen collectors).
译文:即Parallel Scavenge收集器,和Copy收集器类似,不过差别是它采用多线程进行并行收集。Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量(Throughput) 。
Parallel Scavenge收集器提供了两个参数用于精确控制吞吐量, 分别是控制最大垃圾收集停顿时间的-XX:MaxGCPauseMillis参数以及直接设置吞吐量大小的-XX: GCTimeRatio参数。由于与吞吐量关系密切, Parallel Scavenge收集器也经常称为“吞吐量优先” 收集器。 除上述两个参数之外, Parallel Scavenge收集器还有一个参数-XX: +UseAdaptiveSizePolicy值得关注。 这是一个开关参数, 当这个参数打开之后,虚拟机会根据当前系统的运行情况收集性能监控信息, 动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量, 这种调节方式称为GC自适应的调节策略(GC Ergonomics) [1]。
1.3 ParNew (别名和内部名一样为ParNew)
-the parallel copy collector, like the Copy collector, but uses multiple threads in parallel and has an internal ‘callback‘ that allows an old generation collector to operate on the objects it collects (really written to work with the concurrent collector).
ParNew收集器其实就是Serial收集器的多线程版本, 除了使用多条线程进行垃圾收集。
1.4 G1 Young Generation
-the garbage first collector, uses the ‘Garbage First‘ algorithm which splits up the heap into lots of smaller spaces, but these are still separated into Eden and Survivor spaces in the young generation for G1.
2.老年代
2.1.MarkSweepCompact (别名:Serial Old(MSC))
the serial mark-sweep collector, the daddy of them all, uses a serial (one thread) full mark-sweep garbage collection algorithm, with optional compaction.
Serial Old是Serial收集器的老年代版本, 它同样是一个单线程收集器, 使用“标记-整理” 算法。
2.2 PS MarkSweep(别名:Parallel Old )
the parallel scavenge mark-sweep collector, parallelised version (i.e. uses multiple threads) of the MarkSweepCompact.
Parallel Old是Parallel Scavenge收集器的老年代版本, 使用多线程和“标记-整理” 算法。
同样,也是上面MarkSweepCompact 的多线程版本。
2.3 ConcurrentMarkSweep (别名:CMS)
the concurrent collector, a garbage collection algorithm that attempts to do most of the garbage collection work in the background without stopping application threads while it works (there are still phases where it has to stop application threads, but these phases are attempted to be kept to a minimum). Note if the concurrent collector fails to keep up with the garbage, it fails over to the serial MarkSweepCompact collector for (just) the next GC.
CMS(Concurrent Mark Sweep) 收集器是一种以获取最短回收停顿时间为目标的收集器。
CMS收集器工作时,会在后台完成大部分的垃圾收集工作而不停止用户线程(停止用户线程的阶段依然存在,只是会被维持在很低的水平)
CMS运行期间可能失败, 这时虚拟机将启动后备预案: 临时启用Serial Old(MarkSweepCompact )收集器来重新进行老年代的垃圾收集, 这样停顿时间就很长了。
2.4 G1 Mixed Generation
the garbage first collector, uses the ‘Garbage First‘ algorithm which splits up the heap into lots of smaller spaces.
3.总结
All of the garbage collection algorithms except ConcurrentMarkSweep are stop-the-world, i.e. they stop all application threads while they operate - the stop is known as ‘pause‘ time. The ConcurrentMarkSweep tries to do most of it‘s work in the background and minimize the pause time, but it also has a stop-the-world phase and can fail into the MarkSweepCompact which is fully stop-the-world. (The G1 collector has a concurrent phase but is currently mostly stop-the-world).
所有的垃圾回收算法,除了ConcurrentMarkSweep外, 都会stop-the-world,比如他们在进行垃圾回收时,会停止所有的应用程序线程。ConcurrentMarkSweep 试图在后台进行大部分的工作,尽量把停顿时间削减到最小,但是它仍然会有stop-the-world阶段。而且,当ConcurrentMarkSweep 收集失败时,会回退到MarkSweepCompact算法(该算法会完全阻止应用程序的运行)
以上只是简单介绍,详细可以参考书籍或者以下链接:
4.判断当前正在使用的垃圾收集器
如果程序已经处于运行状态,可使用jconsole、jinfo(linux下)查看。
二、新生代老年代之间的收集器组合
That‘s the set of garbage collectors available, but they operate in two different heap spaces and it‘s the combination that is what we actually end up with for a particular JVM setting, so I‘ll also list the combinations that are possible. It doesn‘t explode into a dozen combinations because not all of these collectors work with each other. G1 is effectively an antisocial collector that doesn‘t like working with anyone else; the serial collectors are the "last man picked" collectors; the ‘PS‘ collectors like to work with each other; and ParNew and Concurrent like to work together.
译文:以上即使存在的所有的垃圾收集器,但是他们分别在两块不同的堆区域上运行,针对一个特定的JVM,我们最终需要的内存设置是新生代和老年代的收集器组合。
因此,我将会列举所有可能的收集器组合。这并不会造成组合数量激增,因为并不是所有的垃圾收集器都能愉快地两两组合。G1是最不善社交的收集器,它和任何一个别的收集器都不合群;单线程收集器是那个最后的备胎收集器;多线程"PS"收集器喜欢互相一起玩;ParNew喜欢和CMS一起工作。
The full list of possible GC algorithm combinations that can work are:
Command Options* | Resulting Collector Combination |
---|---|
-XX:+UseSerialGC | young Copy and old MarkSweepCompact |
-XX:+UseG1GC | young G1 Young and old G1 Mixed |
-XX:+UseParallelGC -XX:+UseParallelOldGC -XX:+UseAdaptiveSizePolicy | young PS Scavenge old PS MarkSweep with adaptive sizing |
-XX:+UseParallelGC -XX:+UseParallelOldGC -XX:-UseAdaptiveSizePolicy | young PS Scavenge old PS MarkSweep, no adaptive sizing |
-XX:+UseParNewGC (deprecated in Java 8 and removed in Java 9 - for ParNew see the line below which is NOT deprecated) | young ParNew old MarkSweepCompact |
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC | young ParNew old ConcurrentMarkSweep** |
-XX:+UseConcMarkSweepGC -XX:-UseParNewGC (deprecated in Java 8 and removed in Java 9) | young Copy old ConcurrentMarkSweep** |
*All the combinations listed here will fail to let the JVM start if you add another GC algorithm not listed, with the exception of -XX:+UseParNewGC which is only combinable with -XX:+UseConcMarkSweepGC | |
**there are many many options for use with -XX:+UseConcMarkSweepGC which change the algorithm, e.g.
|
三、收集器组合--通过程序进行参数测试
1.测试程序
import java.lang.management.GarbageCollectorMXBean; import java.lang.management.ManagementFactory; import java.util.List; public class Test { public static void main(String args[]) { List<GarbageCollectorMXBean> l = ManagementFactory.getGarbageCollectorMXBeans(); assert (l != null && l.size() ==2); assert(l.size() == 2); for (int i = 0; i < l.size(); i++) { GarbageCollectorMXBean garbageCollectorMXBean = l.get(i); if (i == 0){ System.out.println("young generation:" + garbageCollectorMXBean.getName()); }else if (i == 1){ System.out.println("old generation:" + garbageCollectorMXBean.getName()); } } } }
2.测试过程
2.0 《深入java虚拟机》中展示的虚拟机组合
以上共有7种组合。我们在下面一一测试。
本机环境:(jdk 1.8,Server模式)
E:javabasesrc>java -XX:+PrintCommandLineFlags -version -XX:InitialHeapSize=126839040 -XX:MaxHeapSize=2029424640 -XX:+PrintCommandLineFl ags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesInd ividualAllocation -XX:+UseParallelGC java version "1.8.0_11" Java(TM) SE Runtime Environment (build 1.8.0_11-b12) Java HotSpot(TM) 64-Bit Server VM (build 25.11-b03, mixed mode)
2.1 默认无参数
E:javabasesrc>java Test
young generation:PS Scavenge
old generation:PS MarkSweep
根据前面的介绍,PS Scavenge对应Parallel Scavenge,PS MarkSweep对应Parallel Old,
即对应上图中的连线组合6.
2.2 -XX:+UseSerialGC
E:javabasesrc>java -XX:+UseSerialGC Test
young generation:Copy
old generation:MarkSweepCompact
对应上图中的连线组合2.
2.3 +UseG1GC
E:javabasesrc>java -XX:+UseG1GC Test
young generation:G1 Young Generation
old generation:G1 Old Generation
对应上图中的连线组合7.
2.4 -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:+UseAdaptiveSizePolicy
E:javabasesrc>java -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:+UseAdaptiveSizePolicy Test
young generation:PS Scavenge
old generation:PS MarkSweep
对应上图中的连线组合5.
2.5 -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:-UseAdaptiveSizePolicy
E:javabasesrc>java -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:-UseAdaptiveSizePolicy Test
young generation:PS Scavenge
old generation:PS MarkSweep
对应上图中的连线组合5.
2.6 -XX:+UseParNewGC
E:javabasesrc>java -XX:+UseParNewGC Test
Java HotSpot(TM) 64-Bit Server VM warning: Using the ParNew young collector with the Serial old collector is deprecated and will likely be removed in a future release
young generation:ParNew
old generation:MarkSweepCompact
对应上图中的连线组合4. 该组合已过期,在未来版本将会移除。
2.7 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC
E:javabasesrc>java -XX:+UseConcMarkSweepGC -XX:+UseParNewGC Test
young generation:ParNew
old generation:ConcurrentMarkSweep
对应上图中的连线组合3
2.8 -XX:+UseConcMarkSweepGC -XX:-UseParNewGC
E:javabasesrc>java -XX:+UseConcMarkSweepGC -XX:-UseParNewGC Test
Java HotSpot(TM) 64-Bit Server VM warning: Using the DefNew young collector with the CMS collector is deprecated and will likely be removed in a future release
young generation:Copy
old generation:ConcurrentMarkSweep
对应上图中的连线组合1. 该组合已过期,在未来版本将会移除。
四、收集器组合--通过jconsole进行参数测试
1.-XX:+UseSerialGC
C:UsersAdministratorDesktop>java -XX:+UseSerialGC -jar javadoc-jar-vi ewer-1.0.jar
2.-XX:+UseG1GC
3.-XX:+UseParallelGC -XX:+UseParallelOldGC -XX:+UseAdaptiveSizePolicy
4.-XX:+UseParallelGC -XX:+UseParallelOldGC -XX:-UseAdaptiveSizePolicy
5.-XX:+UseParNewGC
该选项会有过期的提示:
C:UsersAdministratorDesktop>java -XX:+UseParNewGC -jar javadoc-jar-viewer-1.
0.jar
Java HotSpot(TM) 64-Bit Server VM warning: Using the ParNew young collector with
the Serial old collector is deprecated and will likely be removed in a future r
elease
6.-XX:+UseConcMarkSweepGC -XX:+UseParNewGC
7.-XX:+UseConcMarkSweepGC -XX:-UseParNewGC
该组合选项会有警告提示:
C:UsersAdministratorDesktop>java -XX:+UseConcMarkSweepGC -XX:-UseParNewGC -ja
r javadoc-jar-viewer-1.0.jar
Java HotSpot(TM) 64-Bit Server VM warning: Using the DefNew young collector with
the CMS collector is deprecated and will likely be removed in a future release
五、其他选项(效果同上)
Command Options Used On Their Own | Equivalent To Entry In Table Above |
---|---|
-XX:+UseParallelGC | -XX:+UseParallelGC -XX:+UseParallelOldGC |
-XX:+UseParallelOldGC | -XX:+UseParallelGC -XX:+UseParallelOldGC |
-Xincgc (deprecated in Java 8 and removed in Java 9) | -XX:+UseParNewGC -XX:+UseConcMarkSweepGC |
-XX:+UseConcMarkSweepGC | -XX:+UseParNewGC -XX:+UseConcMarkSweepGC |
no option on most Windows | -XX:+UseG1GC from Java 9, or before that -XX:+UseSerialGC (see also this page) |
no option on most Unix | -XX:+UseG1GC from Java 9, or before that -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:+UseAdaptiveSizePolicy (see also this page) |
-XX:+AggressiveHeap | -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:+UseAdaptiveSizePolicy with a bunch of other options related to sizing memory and threads and how they interact with the OS |
1.-XX:+UseParallelGC
2.-XX:+UseParallelOldGC
3.-Xincgc
C:UsersAdministratorDesktop>java -Xincgc -jar javadoc-jar-viewer-1.0.jar
Java HotSpot(TM) 64-Bit Server VM warning: Using incremental CMS is deprecated a
nd will likely be removed in a future release
4.-XX:+UseConcMarkSweepGC
5.windows上默认无选项时
6.-XX:+AggressiveHeap
六、参考资料
鼠标点选设置jvm参数:http://jvmmemory.com/
以上是关于JVM垃圾收集—垃圾收集器及常见组合参数的主要内容,如果未能解决你的问题,请参考以下文章
直通BAT必考题系列:7种JVM垃圾收集器特点,优劣势及使用场景