查看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为模板设计的,按照我们程序员的说法,估计是拷贝过来,改吧改吧出来的。

所以差不太多。

 

posted @ 2018-06-23 16:10  三国梦回  阅读(18239)  评论(6编辑  收藏