JVM 參數:跟蹤類的加載與卸載
1. 前言
本節內容主要是學習, JVM 跟蹤類的加載與卸載的常用參數配置,這是工作中跟蹤類的加載與卸載情況時 JVM 中最常用的參數配置。本節主要知識點如下:
- 理解并掌握跟蹤類的加載與卸載的參數 -XX:+TraceClassLoading,為本節重點內容;
- 理解并掌握跟蹤類的加載與卸載的參數 -XX:+TraceClassUnloading,為本節了解內容,非重點知識;
- 理解并掌握跟蹤類的加載與卸載的參數 -XX:+PrintClassHistogram,為本節重點內容;
JVM 跟蹤類的加載與卸載的常用參數是使用 JVM 所必須的知識點,通篇皆為重點掌握內容,需要在理解的基礎上并掌握參數的使用方法。
2. 示例代碼準備
此處的示例代碼,與上一節的示例代碼相似,但是有重要的區別。詳細區別請看 Tips 的內容。
實例:準備測試代碼,創建一個 String 類型的 ArrayList,并在 list 中添加三個元素,分別是 “Hello”,“World”,“?。?!”。
Tips:注意,此處的示例代碼,并沒有執行 gc 操作。上一節的內容是為了跟蹤垃圾回收,所以需要手動調用 gc 方法而達到垃圾回收的效果。而此處我們討論的是類的加載與卸載,此處無需進行手動垃圾回收。
public class TracingClassParamsDemo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>();
list.add("Hello");
list.add("World");
list.add("!!!");
}
}
3. -XX:+TraceClassLoading 參數
參數作用:-XX:+TraceClassLoading 參數是為了跟蹤類的加載。
為了更好的理解并掌握 -XX:+TraceClassLoading 參數,我們通過如下步驟進行操作。
- 步驟 1:在 VM Options 中配置參數 -XX:+TraceClassLoading 并保存;
- 步驟 2:運行示例代碼,觀察執行結果。
結果驗證:由于追蹤的結果日志非常龐大,此處僅展示具有代表性的類的加載。全部的類加載日志,請學習者自行執行代碼進行驗證。
[Opened C:\Program Files\Java\jdk1.8.0_152\jre\lib\rt.jar]
[Loaded java.lang.Object from C:\Program Files\Java\jdk1.8.0_152\jre\lib\rt.jar]
[Loaded java.util.ArrayList$SubList from C:\Program Files\Java\jdk1.8.0_152\jre\lib\rt.jar]
[Loaded java.util.ListIterator from C:\Program Files\Java\jdk1.8.0_152\jre\lib\rt.jar]
[Loaded java.util.ArrayList$SubList$1 from C:\Program Files\Java\jdk1.8.0_152\jre\lib\rt.jar]
[Loaded DemoMain.TracingClassParamsDemo from file:/D:/GIT-Repositories/GitLab/Demo/out/production/Demo/]
[Loaded java.lang.Class$MethodArray from C:\Program Files\Java\jdk1.8.0_152\jre\lib\rt.jar]
[Loaded java.lang.Void from C:\Program Files\Java\jdk1.8.0_152\jre\lib\rt.jar]
[Loaded java.lang.Shutdown from C:\Program Files\Java\jdk1.8.0_152\jre\lib\rt.jar]
[Loaded java.lang.Shutdown$Lock from C:\Program Files\Java\jdk1.8.0_152\jre\lib\rt.jar]
結果分析:我們來對類的加載日志進行分析。
- 第一行:Opened rt.jar。打開 rt.jar,rt.jar 全稱是 Runtime,該 jar 包含了所有支持 Java 運行的核心類庫,是類加載的第一步;
- 第二行:加載 java.lang.Object。Object 是所有對象的父類,是首要加載的類;
- 第三、四、五行:加載了 ArrayList 的相關類,我們的示例代碼中使用到了 ArrayList,因此需要對該類進行加載;
- 第六行:加載我們的測試類 TracingClassParamsDemo ;
- 第七行:加載 java.lang.Class 類,并加載類方法 MethodArray;
- 第八行:加載 java.lang.Void 類,因為我們的 main 函數是 void 的返回值類型,所以需要加載此類;
- 第九、十行:加載 java.lang.Shutdown 類, JVM 結束運行后,關閉 JVM 虛擬機。
從以上對日志的分析來看,JVM 對類的加載,不僅僅加載我們代碼中使用的類,還需要加載各種支持 Java 運行的核心類。類加載的日志量非常龐大,此處僅僅對重點類的加載進行日志的解讀,全部的類加載日志,請學習者自行執行代碼進行驗證。
4. -XX:+TraceClassUnloading 參數
參數作用:-XX:+TraceClassUnloading 參數是為了跟蹤類的卸載。由于系統類加載器加載的類不會被卸載,并且只加載一次,所以普通項目很難獲取到類卸載的日志。
此處我們先來看看,通過系統類加載器加載的類是否會被卸載。
為了更好的理解并掌握 -XX:+TraceClassUnloading 參數,我們通過如下步驟進行操作。
- 步驟 1:在 VM Options 中配置參數 -XX:+TraceClassUnloading 并保存;
- 步驟 2:運行示例代碼,觀察執行結果。
結果驗證:未打印日志,未發生類的卸載。
引出問題:為什么看不到跟蹤類卸載的日志呢?
上文提到了,由系統類加載器加載的類不能夠被卸載。所以想要看到跟蹤類卸載的日志,我們需要使用自定義的類加載器。通過自定義的類加載器加載的類,在類不可達的時候,會發生垃圾回收,并卸載該類。
一般情況下,開發過程中很少實現自定義的類加載器,除非有特殊的需求場景需要通過自定義的類加載器進行類的加載,因此此處對 -XX:+TraceClassUnloading 稍作了解即可。
5. -XX:+PrintClassHistogram 參數
參數作用:-XX:+PrintClassHistogram 參數是打印、查看系統中類的分布情況。
為了更好的理解并掌握 -XX:+PrintClassHistogram 參數,我們通過如下步驟進行操作。
- 步驟 1:在 VM Options 中配置參數 -XX:+PrintClassHistogram 并保存;
- 步驟 2:修改示例代碼,在代碼最后添加代碼
Thread.sleep(99999999999L)
,確保 main 函數長時間不能結束執行(當然了,也可以使用 while (true) 語句,進行無限長時間循環來創造這種場景,可自行選擇),以便于觀察類的分布情況;
public class TracingClassParamsDemo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>();
list.add("Hello");
list.add("World");
list.add("!!!");
try {
Thread.sleep(99999999999L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- 步驟 3:運行程序,觀察日志輸出;
- 步驟 4:不中斷 main 函數的運行,將鼠標指針移動到日志輸出的 console 界面并單擊鼠標左鍵,確保鼠標的實時位置在 console 界面。按下鍵盤 Ctrl+Break 鍵,觀察日志輸出。
結果驗證:我們執行步驟 3 時,沒有觀察到日志的輸出。當我們嘗試步驟 4 時,獲取到了日志輸出如下圖所示。
結果分析: 這是系統中類的分布情況,那我們來看下日志中每列的表頭部分代表的意思:
- num:自增的序列號,只是為了標注行數,沒有特殊的意義;
- instances:實例數量,即類的數量;
- bytes:實例所占子節數,即占據的內存空間大小;
- class name:具體的實例。
我們取出第 3 條日志來進行下翻譯:系統當前狀態下,java.lang.String 類型的實例共有 2700 個,共占用空間大小為 64800 bytes。
6. 小結
本小節的重點內容即,我們所講述的三個常用的跟蹤類的加載與卸載參數,學習者需要對這三個常用參數的意義,以及使用方式進行掌握。
需要特別注意第二個參數 -XX:+TraceClassUnloading,在后續講解類加載器的時候,會實現自定義的類加載器,并使用該參數演示類的卸載。通篇皆為重點內容,需要認真對待,掌握本節要點內容。