JVM 可達性分析法
1. 前言
上節課我們結束了運行時數據區的講解,本節課程開始,我們來對執行引擎進行講解,在執行引擎模塊中,首當其沖的就是垃圾回收器。本節主要知識點如下:
- 了解垃圾回收器在 JVM 整體架構中的位置,為本節基礎知識;
- 了解并掌握垃圾回收器的定義以及意義,為本節基礎知識;
- 理解并掌握可達性分析法的原理以及意義,為本節課程的核心知識點。
2. 垃圾回收器的位置
我們在 JVM 整體架構介紹的小節提到過 JVM 的垃圾回收器位于執行引擎中。而當時我們的執行引擎只是簡單的畫了下,那么我們先來看下執行引擎的更細致的結構如下圖:
Tips:在講解可達性分析之前,我們先來解決一些基本問題:什么是垃圾回收器?為什么進行垃圾回收?哪些內存需要回收?
3. 垃圾回收器的基本概念
什么是垃圾回收器:JVM 為 Java 提供了垃圾回收機制,其實是一種偏自動的內存管理機制。簡單來說,垃圾回收器會自動追蹤所有正在使用的對象,并將其余未被使用的對象標記為垃圾,不需要開發者手動進行垃圾回收,JVM 自動進行垃圾回收,釋放內容。
為什么進行垃圾回收:如果不進行垃圾回收,內存遲早都會被消耗空,因為我們在不斷的分配內存空間而不進行回收。除非內存無限大,我們可以任性的分配不回收,但是事實并非如此。所以,垃圾回收是必須的。
哪些內存需要回收:哪些內存需要回收是垃圾回收機制第一個要考慮的問題,所謂“要回收的垃圾”無非就是那些不可能再被任何途徑所使用的對象。無需再使用的對象,會被標記為垃圾,等待JVM回收此部分內存。
Tips:Java中通過可達性分析法來檢測對象是否為垃圾,如果不可達,則將對象標記為垃圾,會被 JVM 回收,接下來我們學習可達性分析法。
4. 可達性分析法基本原理
方法原理:通過一系列稱為"GC Roots"的對象作為起始點,從這些節點向下搜索,搜索所走過的路徑稱為引用鏈(即GC Roots到對象不可達時),則證明此對象是不可用的。
那么問題又來了,如何選取 GCRoots 對象呢?在 Java 語言中,可以作為 GCRoots 的對象包括下面幾種:
- 虛擬機棧(棧幀中的局部變量區,也叫局部變量表)中引用的對象;
- 方法區中的類靜態變量屬性引用的對象;
- 方法區中常量引用的對象;
- 本地方法棧中 JNI(Native方法)引用的對象。
Tips:看了如上的原理與 GC Roots 選擇的描述,感覺概念性問題比較抽象,難于理解,我們繼續通過示例來進一步理解可達性分析法。
4. 可達性分析法示例
上文中提到了,可達性分析法是通過 GC Roots 為起點的搜索,有四種對象可以作為 GC Roots,那么我們通過如下示意圖來理解下,何為不可達對象。
GC Roots 四種類型解釋:從上圖中,我們可以看到四種 GC Roots。這里我們對這四種 GC Roots 做一下更為細致的解釋。
-
虛擬機棧中的引用的對象:我們在程序中正常創建一個對象,對象會在堆上開辟一塊空間,同時會將這塊空間的地址作為引用保存到虛擬機棧中,如果對象生命周期結束了,那么引用就會從虛擬機棧中出棧,因此如果在虛擬機棧中有引用,就說明這個對象還是有用的,這種情況是最常見的;
-
全局的靜態的對象:也就是使用了 static 關鍵字,由于虛擬機棧是線程私有的,所以這種對象的引用會保存在共有的方法區中,顯然將方法區中的靜態引用作為 GC Roots 是必須的;
-
常量引用:就是使用了 static final 關鍵字,由于這種引用初始化之后不會修改,所以方法區常量池里的引用的對象也應該作為 GC Roots;
-
Native 方法引用對象:這一種是在使用 JNI 技術時,有時候單純的 Java 代碼并不能滿足我們的需求,我們可能需要在 Java 中調用 C 或 C++ 的代碼,因此會使用 native 方法,JVM 內存中專門有一塊本地方法棧,用來保存這些對象的引用,所以本地方法棧中引用的對象也會被作為 GC Roots。
從上圖來理解可達性分析法就會非常簡單,四種 GC Roots 無非是 Java 中的引用對象,從GC Roots 出發,類似于我們使用開發工具看代碼,發現某部分代碼用不到了,我們就會刪除這部分代碼。其實可達性分析法也是如此,發現某些對象不可達了,就會被垃圾回收器收集。
從上圖中來看,對象 A,B,C,D,E,F 為可達對象;而對象 G,H,I,J,K 為不可達對象,會被標記為垃圾對象,最終被垃圾回收器回收。
5. 小結
本節講解了垃圾回收回收器的定義以及垃圾回收器存在的意義,并在此基礎上講解了垃圾回收器是如何判定對象的可達性的??蛇_性分析法是本節的核心知識點,是必須要掌握的知識點。
在講解 GC Roots 的知識點時,我們總是會使用 “引用對象” 這四個字,其中引用又分為 4 種引用,下節課程我們會詳細的講解。