1. 程式人生 > >Java記憶體回收知識(讀書筆記)--深入理解Java虛擬機器——JVM高階特性與最佳實踐(第2版)2.2~2.3

Java記憶體回收知識(讀書筆記)--深入理解Java虛擬機器——JVM高階特性與最佳實踐(第2版)2.2~2.3

1.哪些地方的記憶體要回收?

Java程式運時的記憶體包括以下幾部分:程式計數器,Java虛擬機器棧,本地方法棧,Java堆,方法區(執行時常量池是方法區的一部分)。

程式計數器,Java虛擬機器棧,本地方法棧是隨執行緒而生,隨執行緒而亡,它們的分配的記憶體大小已知,因此不需要開發人員考慮對這些部分的記憶體進行回收,因為方法結束或者執行緒結束的時候,記憶體自然會隨之回收。

Java堆和方法區則是需要開發人員考慮回收的記憶體區域。這部分的記憶體和回收都是動態的,垃圾收集器關注的是這部分記憶體。

2.如何判斷堆記憶體中的物件仍然存活?

主流的方法是通過可達性分析演算法來判斷物件是否存活。這個演算法的基本思路是通過一系列的稱為“GC Root”的物件作為起始點。從這些節點開始向下搜尋,搜尋走過的路徑稱為引用鏈(ReferenceChain),當一個物件到GC Root沒有任何引用鏈相連的時候,這個物件就是不可達的,這個物件就會被判斷為可回收物件。圖示如下:


在Java語言中可以作為GC Root的物件包括以下幾種,

1)  虛擬機器棧(棧幀中的本地變量表)中的引用物件。

2)  方法區中類靜態屬性引用的物件。

3)  方法區中常量引用的物件。

4)  本地方法棧中的JNI(本地方法也稱為Native方法)引用的物件。

3.什麼時候回收?

之前已經討論過,程式計數器,Java虛擬機器棧,本地方法棧是隨執行緒而生,隨執行緒而亡,它們的分配的記憶體大小已知,因此不需要開發人員考慮對這些部分的記憶體進行回收,因為方法結束或者執行緒結束的時候,記憶體自然會隨之回收。因此,這裡說的什麼時候回收記憶體主要是針對Java堆和方法區。

Java堆中存放幾乎存放了所以的例項物件。垃圾回收器在對堆記憶體進行回收之前要檢查哪些物件仍然存活,哪些物件已經死亡(不可能再被任何途徑使用的物件)。

Java方法區用於儲存被虛擬機器載入的類資訊,常量,靜態變數,即使編譯器編譯後的程式碼等,垃圾回收器對這一區域的的回收主要是針對常量池的回收和對類的解除安裝。

所以,如果是非靜態的基本型別,基本型別是存放到Java虛擬機器棧中的,如果該物件的被銷燬的時候,非靜態的基本型別的成員變數也會被回收。但是常量和靜態變數是儲存在方法區,即使該物件被銷燬了,但是該物件裡面的常量和靜態成員變數也是不會被回收的,只有等到整個程序被銷燬的時候常量和靜態變數才會被銷燬。

至於物件,一個物件被宣佈死亡至少要經歷兩個標記過程。第一個是進行可達性分析演算法後,如果發現物件沒有與GC Root相連的引用鏈,那麼他會被第一次標記並進行篩選,篩選條件是此物件是否有必要執行finalize方法。如果被判斷為有必要執行finalize且在這個方法執行的過程中,物件沒能和引用鏈的任意一個物件建立連線,那麼這個物件就基本上是被回收了。

4.Java引用的概念

在JDK1.2之前,Java引用的概念很傳統:如果reference型別的資料儲存的數值代表的是另一塊記憶體的起始地址,就稱這塊記憶體代表著一個引用。雖然這個定義很純粹,但是過於狹隘,一個物件在這種定義下只有被引用和沒有被引用兩種狀態。

因此,在JDK1.2之後,Java對引用的概念進行了擴充,將引用分為強引用(Strong Reference),軟引用(Soft Reference),弱引用(Weak Reference),虛引用(Phantom Reference)4種。這4種引用強度依次逐漸減弱。

1)  強引用就是指類似“Object obj = newObject();”這類引用,只要強引用還存在,垃圾回收器永遠不會回收掉被引用的物件。

2)  軟引用是用來描述一些還有用但是非必須的物件,對於軟引用關聯的物件,在系統將要發生記憶體溢位之前,將會把這些物件列入回收範圍之中進行二次回收,如果這次回收還沒有足夠的記憶體,才會丟擲記憶體溢位異常。JDK1.2之後提供SoftReference類來實現軟引用。

3)  弱引用也是用來描述非必須的物件的,但它的引用強度比軟引用更弱一點,被弱引用關聯的物件只能生存到下一次垃圾回收發生之前。也就是說,當垃圾回收發生的時候,無論當前記憶體是否足夠。被弱引用關聯的物件都會被回收。JDK1.2之後提供WeakReference類來實現弱引用。

4)  虛引用是最弱的一種引用關係。一個物件是否有虛引用的存在,完全不會對其生存時間構成影響,也無法通過虛引用來獲取一個物件的例項。為一個物件設定虛引用關聯的唯一目的就是能在這個物件被收集器回收前收到一個系統通知。JDK1.2之後提供PhantomReference類來實現虛引用。

5.回收方法區

方法區的垃圾回收主要回收兩部分內容:廢棄常量和無用的類。以常量池中字面量的回收為例。假如一個字串“abc”已經進入了常量池(String是被final修飾的,所以它是存在於方法區中),但是當前程式中沒有任何一個String物件是叫做“abc”的,換句話說,就是沒有任何物件引用常量池中的“abc”常量,也沒有其他地方引用了這個字面量,如果這時候發生了記憶體回收,而且必要,這個“abc”常量就會被系統清理出常量池,常量池中的其他類(介面),方法,欄位的符號引用也與此類似。

要判斷一個類是否是“無用的類”條件比較苛刻。這個類需要同時滿足下面三個條件才能算是“無用的類”。

1)  該類的所以例項都已經被回收,也就是Java堆中不存在該類的任何例項。

2)  載入該類的ClassLoader已經被回收。

3)  該類對應的java.lang.Class物件沒有在任何地方被引用。無法在任何地方通過反射訪問該類的方法。

滿足以上三個條件虛擬機器才可以對這個類進行回收,但不一定會回收,只是可以回收。