首頁/ 汽車/ 正文

JVM最佳化案例實戰-手動模擬Young GC

手動模擬發生Young GC

本文將透過設定固定的堆記憶體、新生代等記憶體空間大小,寫程式碼去手動觸發YoungGC,然後根據打印出的GC log日誌去一步一步剖析整個流程。

我們先來設定JVM引數。

-XX:NewSize=5242880 -XX:MaxNewSize=5242880 -XX:InitialHeapSize=10485760 -XX:MaxHeapSize=10485760 -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=10485760 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc。log複製程式碼

這些引數的解釋如下:

XX:NewSize和XX:MaxNewSize初始新生代和最大新生代的大小,為5MB

XX:InitialHeapSize和XX:MaxHeapSize:初始堆記憶體大小和最大堆記憶體大小,為10MB

XX:SurvivorRatio:新生代中Eden區和Survivor區的大小比例,8代表Eden區佔整個新生代區域的80%

XX:PretenureSizeThreshold:指定大物件的閾值是10MB

UseParNewGC新生代用的是ParNewGC垃圾回收器

XX:+UseConcMarkSweepGC老年代用的是CMS垃圾回收器

XX:+PrintGCDetails:列印詳細的GC日誌

XX:+PrintGCTimeStamps:打印出每次GC發生的時間

Xloggc:gc。log:將GC日誌寫入gc。log檔案中

相當於給堆記憶體分配了10MB空間,新生代大小為5MB,其中Eden區佔4MB,兩個Survivor區分別佔0。5MB();老年代大小為5MB,大物件必須超過10MB才直接進入老年代

模擬程式碼:

public static void main(String[] args) { byte[] array1 = new byte[1024 * 1024]; //1MB array1 = new byte[1024 * 1024];//1MB array1 = new byte[1024 * 1024];//1MB array1 = null; byte[] array2 = new byte[2 * 1024 * 1024]; }複製程式碼

程式碼解析:

程式碼行1:byte[] array1 = new byte[1024 * 1024]; 在新生代Eden區分配了1MB的空間,用來儲存陣列:new byte[1024 * 1024],並且在main方法棧內,array1指標指向該陣列物件

JVM最佳化案例實戰-手動模擬Young GC

\

\

程式碼行2:array1 = new byte[1024 * 1024]; 在新生代Eden區又分配了1MB的空間,用來儲存陣列:new byte[1024 * 1024],並且array1指向新的陣列,原來的陣列成為了垃圾物件

JVM最佳化案例實戰-手動模擬Young GC

\

程式碼行3:array1 = new byte[1024 * 1024]; 又重新分配了一個數組空間,並且array1指向最新的陣列物件,此時Eden區內有兩個垃圾物件

JVM最佳化案例實戰-手動模擬Young GC

\

程式碼行4:array1 = null; array1不指向任何物件,則之前分配的三個陣列物件都成為垃圾物件

程式碼行5:byte[] array2 = new byte[2 * 1024 * 1024]; 在Eden區內分配2MB的空間。但是由於之前在Eden區已經有三個物件,佔用了3MB空間,Eden本身只有4MB,需要再分配2MB很明顯空間不夠,會觸發Young GC

執行程式碼後,會在目錄下生成gc。log日誌檔案,我們可以根據這個檔案檢視GC回收的具體細節,這是我們做JVM調優的基礎。

GC日誌解析

GC日誌如下:

Java HotSpot(TM) 64-Bit Server VM (25。321-b07) for bsd-amd64 JRE (1。8。0_321-b07), built on Dec 15 2021 19:12:29 by “java_re” with gcc 4。2。1 Compatible Apple LLVM 11。0。0 (clang-1100。0。33。17)Memory: 4k page, physical 16777216k(45636k free)/proc/meminfo:CommandLine flags: -XX:InitialHeapSize=10485760 -XX:MaxHeapSize=10485760 -XX:MaxNewSize=5242880 -XX:NewSize=5242880 -XX:OldPLABSize=16 -XX:PretenureSizeThreshold=10485760 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:SurvivorRatio=8 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC -XX:+UseParNewGC 0。125: [GC (Allocation Failure) 0。125: [ParNew: 3596K->420K(4608K), 0。0047305 secs] 3596K->1446K(9728K), 0。0053325 secs] [Times: user=0。01 sys=0。00, real=0。01 secs] Heap par new generation total 4608K, used 3642K [0x00000007bf600000, 0x00000007bfb00000, 0x00000007bfb00000) eden space 4096K, 78% used [0x00000007bf600000, 0x00000007bf925a40, 0x00000007bfa00000) from space 512K, 82% used [0x00000007bfa80000, 0x00000007bfae9048, 0x00000007bfb00000) to space 512K, 0% used [0x00000007bfa00000, 0x00000007bfa00000, 0x00000007bfa80000) concurrent mark-sweep generation total 5120K, used 1026K [0x00000007bfb00000, 0x00000007c0000000, 0x00000007c0000000) Metaspace used 3216K, capacity 4496K, committed 4864K, reserved 1056768K class space used 358K, capacity 388K, committed 512K, reserved 1048576K複製程式碼

下面我們就來分析下GC日誌。

CommandLine flags

本次程式碼執行的JVM引數,其中有我們設定的JVM引數,也有系統預設的設定引數

Young GC:

0。125: [GC (Allocation Failure) 0。125: [ParNew: 3596K->420K(4608K), 0。0047305 secs] 3596K->1446K(9728K), 0。0053325 secs] [Times: user=0。01 sys=0。00, real=0。01 secs]複製程式碼

這一行是我們需要分析的重點。

0。125: [GC (Allocation Failure) 0。125 表示在系統執行0。125S後,記憶體分配失敗,為什麼會記憶體分配失敗呢?看我們之前的程式碼,Eden區總的4MB空間,之前已經有3個垃圾物件,最後需要分配2MB的空間,就會發生記憶體不夠,因此分配失敗,此時就會觸發一次Young GC,

[ParNew: 3596K->420K(4608K), 0。0047305 secs]

ParNew表示使用的是年輕代的ParNew 垃圾回收器來進行垃圾回收,後面的3596K->420K表示,回收之前的新生代大小為3。5MB,回收之後為420K約等於0。5MB,相當於本次回收大概回收了3MB的垃圾物件。0。0047305 secs表示本次GC回收耗時。

新生代區的總可用大小為4MB+0。5MB=4。5MB,4。5MB呢就是Eden區大小+一個Survivor區的大小,因為兩個Survivor區一個需要用來存放存活物件,另一個必須保持空閒,所以總可用大小包括了Eden區大小+空閒Survivor區大小。

3596K->1446K(9728K), 0。0053325 secs]表示整個堆記憶體空間的使用情況,總的大小為9728K也就是9。5MB,為什麼不是10MB呢 ?其實就是去除了一個Survivor區域的記憶體空間。在進行GC回收之前堆記憶體空間是3596K,回收後是1446K。

Heap:

Heap par new generation total 4608K, used 3642K [0x00000007bf600000, 0x00000007bfb00000, 0x00000007bfb00000) eden space 4096K, 78% used [0x00000007bf600000, 0x00000007bf925a40, 0x00000007bfa00000) from space 512K, 82% used [0x00000007bfa80000, 0x00000007bfae9048, 0x00000007bfb00000) to space 512K, 0% used [0x00000007bfa00000, 0x00000007bfa00000, 0x00000007bfa80000) concurrent mark-sweep generation total 5120K, used 1026K [0x00000007bfb00000, 0x00000007c0000000, 0x00000007c0000000) Metaspace used 3216K, capacity 4496K, committed 4864K, reserved 1056768K class space used 358K, capacity 388K, committed 512K, reserved 1048576K複製程式碼

這些日誌是在JVM退出前打印出的當時JVM堆記憶體使用的情況。

包括使用ParNew GC的新生代、Eden、From Survivor區、To Survivor區、使用CMS的老年代、元空間等的記憶體使用情況。

相關文章

頂部