查看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為模板設計的,按照我們程序員的說法,估計是拷貝過來,改吧改吧出來的。

所以差不太多。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM