查看JVM使用的默认的垃圾收集器
一、查看步骤
cmd执行命令:
java -XX:+PrintCommandLineFlags -version
输出如下(举例):
针对上述的-XX:UseParallelGC,这边我们引用《深入理解Java虚拟机:JVM高级特性与最佳实践》的介绍:
也就是说,打开此开关,使用的垃圾收集器是:新生代(Parallel Scavenge),老年代(Ps MarkSweep)组合。
二、更新于2020-07-14
本文自发表以来,阅读量慢慢还在增加,一般来说,发表后,文章流量就稳定了,至于越来越高,这说明不少是搜索引擎搜索到来的。
受当时的见解所限,原来的分析,是有些问题的,是不充分的,因为jconsole/jvisualvm的面板,不一定对。
这边有位同学,在评论里贴了一个链接,
https://www.zhihu.com/question/56344485
这里链接里,一楼有R大的回答,大家参考那个答案即可。
R大的意思是,自JDK7u4开始的JDK7u系列与JDK8系列,如果指定了:-XX:+UseParallelGC,则会默认开启:
XX:+UseParallelOldGC 。
我这里自己也做了个实验,jvm参数如下:
-Xms100M -Xmx100M -Xmn70M -XX:PretenureSizeThreshold=5M -XX:+UseParallelGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintGCCause -Xloggc:gc-old.log -verbose:gc
代码demo很简单,就是个普通的spring boot 的web程序:
@SpringBootApplication @RestController @Slf4j public class OrderServiceApplication { static Object object; static List<Object> list = new ArrayList<>(); public static void main(String[] args) { SpringApplication.run(OrderServiceApplication.class, args); } @RequestMapping("/addlist") public void lust(@RequestParam("value") Integer value) throws InterruptedException { byte[] bytes = new byte[value * 1024 * 1024]; list.add(bytes); } @RequestMapping("/clearlist") public void clearlist() throws InterruptedException { list.clear(); } }
pom.xml如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.7.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>garbage-collection-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>garbage-collection-demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Greenwich.SR3</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.10</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
我们这里,总共的堆是100m,其中新生代(eden + s0/s1共70m,老年代30m):
然后我们不断地调用
http://localhost:8082/addlist?value=10
很容易就能触发old gc。
此时,查看我们的gc日志,下边红色我标出了ParOldGen字样:
2020-07-13T23:59:26.190+0800: 287.077: [GC (Allocation Failure) --[PSYoungGen: 50176K->50176K(60928K)] 75372K->75380K(91648K), 0.0107778 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 2020-07-13T23:59:26.201+0800: 287.088: [Full GC (Ergonomics) [PSYoungGen: 50176K->49245K(60928K)] [ParOldGen: 25204K->25196K(30720K)] 75380K->74441K(91648K), [Metaspace: 35737K->35737K(1081344K)], 0.0611277 secs] [Times: user=0.08 sys=0.00, real=0.06 secs] 2020-07-13T23:59:30.196+0800: 291.082: [GC (Allocation Failure) --[PSYoungGen: 50176K->50176K(60928K)] 75372K->80492K(91648K), 0.0075458 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 2020-07-13T23:59:30.204+0800: 291.090: [Full GC (Ergonomics) [PSYoungGen: 50176K->44169K(60928K)] [ParOldGen: 30316K->30316K(30720K)] 80492K->74485K(91648K), [Metaspace: 35737K->35737K(1081344K)], 0.0816321 secs] [Times: user=0.09 sys=0.02, real=0.08 secs]
按照R大的说法,那就是启用了Parallel Old这个老年代收集器。
所以,这里我们就可以得出结论:
在默认情况下,会开启-XX:UseParallelGC参数,此时,新生代使用了Parallel New ,老年代使用了Parallel Old。
我这边的版本是java 1.8:
C:\Windows\system32>java -version 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)
实验二:
切换jvm参数为如下后(去掉了-XX:+UseParallelGC,增加了-server):
-Xms100M
-Xmx100M
-Xmn70M
-XX:PretenureSizeThreshold=5M
-server
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintGCDateStamps
-XX:+PrintGCCause
-Xloggc:gc-old.log
-verbose:gc
结果没什么变化:
2020-07-14T00:18:57.133+0800: 15.023: [Full GC (Metadata GC Threshold) [PSYoungGen: 8679K->0K(58880K)] [ParOldGen: 8275K->12677K(30720K)] 16954K->12677K(89600K), [Metaspace: 33761K->33761K(1079296K)], 0.0882966 secs] [Times: user=0.19 sys=0.00, real=0.09 secs] 2020-07-14T00:19:48.649+0800: 66.537: [GC (Allocation Failure) --[PSYoungGen: 48890K->48890K(58880K)] 61568K->71808K(89600K), 0.0158096 secs] [Times: user=0.03 sys=0.00, real=0.02 secs] 2020-07-14T00:19:48.665+0800: 66.553: [Full GC (Ergonomics) [PSYoungGen: 48890K->12213K(58880K)] [ParOldGen: 22917K->21159K(30720K)] 71808K->33373K(89600K), [Metaspace: 35756K->35756K(1081344K)], 0.1872180 secs] [Times: user=0.48 sys=0.00, real=0.19 secs]
实验三:
在使用以下参数时(使用了-XX:+UseSerialGC):
-Xms100M -Xmx100M -Xmn70M -XX:PretenureSizeThreshold=5M -client -XX:+UseSerialGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintGCCause -Xloggc:gc-old.log -verbose:gc
gc日志如下(gc日志开头会输出本次使用的jvm参数,大家可以注意:)
Java HotSpot(TM) 64-Bit Server VM (25.11-b03) for windows-amd64 JRE (1.8.0_11-b12), built on Jun 16 2014 20:57:32 by "java_re" with MS VC++ 10.0 (VS2010) Memory: 4k page, physical 12121744k(4890584k free), swap 14067940k(5380188k free) CommandLine flags: -XX:InitialHeapSize=104857600 -XX:MaxHeapSize=104857600 -XX:MaxNewSize=73400320 -XX:NewSize=73400320
-XX:PretenureSizeThreshold=5242880 -XX:+PrintGC -XX:+PrintGCCause -XX:+PrintGCDateStamps
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UseCompressedClassPointers -XX:+UseCompressedOops
-XX:-UseLargePagesIndividualAllocation -XX:+UseSerialGC 2020-07-14T00:26:57.301+0800: 1.888: [GC (Allocation Failure) 1.888: [DefNew: 57344K->6800K(64512K), 0.0209500 secs] 57344K->6800K(95232K), 0.0211331 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 2020-07-14T00:26:57.984+0800: 2.570: [GC (Allocation Failure) 2.570: [DefNew: 64144K->6072K(64512K), 0.0196770 secs] 64144K->8975K(95232K), 0.0197613 secs] [Times: user=0.03 sys=0.00, real=0.02 secs] 2020-07-14T00:26:58.270+0800: 2.856: [Full GC (Metadata GC Threshold) 2.856: [Tenured: 2903K->7142K(30720K), 0.0417688 secs] 34782K->7142K(95232K), [Metaspace: 20739K->20739K(1067008K)], 0.0418646 secs] [Times: user=0.03 sys=0.00, real=0.04 secs]
此时,新生代是DefNew,老年代是Tenured,明显和前面使用了 -XX:+UseParallelGC时候不一样。
-----------2020-07-14更新结束,以下为原答案,随意看看就行。
二、验证下,是不是那么回事吧
我用ide起了一个程序,然后在main中进行长时间睡眠。启动时,设置其VM 参数如下:
然后用Jconsole连接该程序,切换到VM概要这个tab,注意下图红圈圈出来的地方:
结合第一步中的资料,很容易验证,使用-XX:UseParallelGC的情况下,使用的垃圾收集器为:新生代(Ps Scanvenge),老年代(Ps MarkSweep,与Serial Old)。
三、Ps Scanvenge的简要介绍
这边附上我的简单理解:该垃圾收集器适用于新生代,采用标记复制算法、多线程模型进行垃圾收集。
与其他新生代垃圾收集器的差别是,它更关注于吞吐量,而不是停顿时间。一般来说,需要与用户交互的
程序更关注较短的停顿时间,而如果是需要达成尽量大的吞吐量的话,则该处理器会更加适合。
其通过-XX:UseAdaptiveSizePolicy参数,可以开启其自动调节功能,适用于对垃圾收集器的调优不太了解的
用户。
四、Serial Old的简要介绍
我的理解:和其他老年代垃圾处理器一样,都是使用的标记整理算法,(毕竟没有靠山可以担保,没法复制,只能自己整理了,哎),
采用单线程处理模型。
五、Serial Old和Ps MarkSweep的区别
如上图所示,也说了,在实际中,(正如第二节的截图所示),实际应用中,大多使用的就是Ps MarkSweep。
Ps MarkSweep是以Serial Old为模板设计的,按照我们程序员的说法,估计是拷贝过来,改吧改吧出来的。
所以差不太多。