Java基礎:JVM垃圾回收算法

眾所周知,Java的垃圾回收是不需要程序員去手動操控的,而是由JVM去完成。本文介紹JVM進行垃圾回收的各種算法。

1. 如何確定某個對象是垃圾

1.1. 引用計數法

在Java中,引用和對象是有關聯的。如果要操作對象則必須用引用進行。因此,很顯然一個簡單的辦法是通過引用計數來判斷一個對象是否可以回收。簡單說,即一個對象如果沒有任何與之關聯的引用,則說明對象不太可能再被用到,那么這個對象就是可回收對象。這種方式即是引用計數法。這種方式的問題是無法解決循環引用的問題,舉個例子:

public static void main(String[] args){
    Object object1=new Object();
    Object object2=new Object();
    object1.object=object2;
    object2.object=object1;
    object1=null;
    object2=null;
}

顯然,在最后,object1和object2的內存塊都不能再被訪問到了,但他們的引用計數都不為0,這就會使他們永遠不會被清除。

1.2. 可達性分析

為了解決引用計數法的循環引用問題,Java使用了可達性分析的方法。通過一系列的“GC roots”對象作為起點搜索。如果在“GC roots”和一個對象之間沒有可達路徑,則稱該對象是不可達的。要注意的是,不可達對象不等價于可回收對象,不可達對象變為可回收對象至少要經過兩次標記過程。兩次標記后仍然是可回收對象,則將面臨回收。

所謂“GC roots”,或者說tracing GC的“根集合”,就是一組必須活躍的引用。例如說,這些引用可能包括:

  • 所有Java線程當前活躍的棧幀里指向GC堆里的對象的引用;換句話說,當前所有正在被調用的方法的引用類型的參數/局部變量/臨時值。
  • VM的一些靜態數據結構里指向GC堆里的對象的引用,例如說HotSpot VM里的Universe里有很多這樣的引用。
  • JNI handles,包括global handles和local handles(看情況)
  • 所有當前被加載的Java類(看情況)
  • Java類的引用類型靜態變量(看情況)
  • Java類的運行時常量池里的引用類型常量(String或Class類型)(看情況)
  • String常量池(StringTable)里的引用

比較常見的將對象視為可回收對象的原因:

  • 顯式地將對象的唯一強引用指向新的對象。
  • 顯式地將對象的唯一強引用賦值為Null。
  • 局部引用所指向的對象(如,方法內對象)。

下述代碼中,每次循環結束,object都會被視為可回收對象。

void fun() {
 
.....
    for(int i=0;i<10;i++) {
        Object obj = new Object();
        System.out.println(obj.getClass());
    }   
}
  • 只有弱引用與其關聯的對象

2. 典型的垃圾回收算法

在JVM規范中并沒有明確GC的運作方式,各個廠商可以采用不同的方式去實現垃圾回收器。這里討論幾種常見的GC算法。

2.1. 標記-清除算法(Mark-Sweep)

最基礎的垃圾回收算法,分為兩個階段,標注和清除。標記階段標記出所有需要回收的對象,清除階段回收被標記的對象所占用的空間。如圖:

image

從圖中我們就可以發現,該算法最大的問題是內存碎片化嚴重,后續可能發生大對象不能找到可利用空間的問題。

2.2. 復制算法(Copying)

為了解決Mark-Sweep算法內存碎片化的缺陷而被提出的算法。按內存容量將內存劃分為等大小的兩塊。每次只使用其中一塊,當這一塊內存滿后將尚存活的對象復制到另一塊上去,把已使用的內存清掉,如圖:

image

這種算法雖然實現簡單,內存效率高,不易產生碎片,但是最大的問題是可用內存被壓縮到了原本的一半。且存活對象增多的話,Copying算法的效率會大大降低。

2.3. 標記-整理算法(Mark-Compact)

結合了以上兩個算法,為了避免缺陷而提出。標記階段和Mark-Sweep算法相同,標記后不是清理對象,而是將存活對象移向內存的一端。然后清除端邊界外的對象。如圖:

image

2.4. 分代收集算法(Generational Collection)

分代收集法是目前大部分JVM所采用的方法,其核心思想是根據對象存活的不同生命周期將內存劃分為不同的域,一般情況下將GC堆劃分為老生代(Tenured/Old Generation)和新生代(Young Generation)。老生代的特點是每次垃圾回收時只有少量對象需要被回收,新生代的特點是每次垃圾回收時都有大量垃圾需要被回收,因此可以根據不同區域選擇不同的算法。

目前大部分JVM的GC對于新生代都采取Copying算法,因為新生代中每次垃圾回收都要回收大部分對象,即要復制的操作比較少,但通常并不是按照1:1來劃分新生代。一般將新生代劃分為一塊較大的Eden空間和兩個較小的Survivor空間(From Space, To Space),每次使用Eden空間和其中的一塊Survivor空間,當進行回收時,將該兩塊空間中還存活的對象復制到另一塊Survivor空間中。

image

而老生代因為每次只回收少量對象,因而采用Mark-Compact算法。

另外,不要忘記在Java基礎:Java虛擬機(JVM)中提到過的處于方法區的永生代(Permanet Generation)。它用來存儲class類,常量,方法描述等。對永生代的回收主要包括廢棄常量和無用的類。

對象的內存分配主要在新生代的Eden Space和Survivor Space的From Space(Survivor目前存放對象的那一塊),少數情況會直接分配到老生代。當新生代的Eden Space和From Space空間不足時就會發生一次GC,進行GC后,Eden Space和From Space區的存活對象會被挪到To Space,然后將Eden Space和From Space進行清理。如果To Space無法足夠存儲某個對象,則將這個對象存儲到老生代。在進行GC后,使用的便是Eden Space和To Space了,如此反復循環。當對象在Survivor區躲過一次GC后,其年齡就會+1。默認情況下年齡到達15的對象會被移到老生代中。

3. 典型的垃圾收集器

垃圾收集算法是垃圾收集器的理論基礎,而垃圾收集器就是其具體實現。下面介紹HotSpot虛擬機提供的幾種垃圾收集器。

3.1. Serial/Serial Old

最古老的收集器,是一個單線程收集器,用它進行垃圾回收時,必須暫停所有用戶線程。Serial是針對新生代的收集器,采用Copying算法;而Serial Old是針對老生代的收集器,采用Mark-Compact算法。優點是簡單高效,缺點是需要暫停用戶線程。

3.2. ParNew

Seral/Serial Old的多線程版本,使用多個線程進行垃圾收集。

3.3. Parallel Scavenge

新生代的并行收集器,回收期間不需要暫停其他線程,采用Copying算法。該收集器與前兩個收集器不同,主要為了達到一個可控的吞吐量。

3.4. Parallel Old

Parallel Scavenge的老生代版本,采用Mark-Compact算法和多線程。

3.5. CMS

Current Mark Sweep收集器是一種以最小回收時間停頓為目標的并發回收器,因而采用Mark-Sweep算法。

3.6. G1

G1(Garbage First)收集器技術的前沿成果,是面向服務端的收集器,能充分利用CPU和多核環境。是一款并行與并發收集器,它能夠建立可預測的停頓時間模型。

4. 參考文章

Java垃圾回收機制
java的gc為什么要分代? - RednaxelaFX的回答 - 知乎

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,983評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,772評論 3 422
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,947評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,201評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,960評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,350評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,406評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,549評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,104評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,914評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,089評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,647評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,340評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,753評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,007評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,834評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,106評論 2 375

推薦閱讀更多精彩內容