文章目錄
- 前言
- 一、如何判斷一個對象是否為垃圾?
- 1.1、reference count(引用計數)
- 1.2、reference count(引用計數)存在的問題
- 二、Root Searching(根可達算法或根搜索算法)
- 2.1、Root Searching 釋義
- 2.2、根對象(root)的類型
- 三、三色標記算法原理與存在的問題
- 3.1、Mark-Sweep(標記清除)
- 3.1.1、Mark-Sweep(標記清除)應用原理
- 3.1.2、存在問題-內存碎片化
- 3.2、Copying(拷貝)
- 3.2.1、Copying(拷貝)應用原理
- 3.2.2、存在問題-浪費空間
- 3.3、Mark-Compact(標記壓縮或標記整理)
- 3.3.1、Mark-Compact(標記壓縮或標記整理)應用原理
- 3.3.2、存在的問題-效率過低
- 四、垃圾回收器的制定原則
- 4.1、綜合三種算法的 GC
- 4.2、新生代里面對象的 age 要取值多少?
- 4.3、堆內存邏輯分區介紹(適用分代垃圾回收器)
- 4.4、為什么年輕代用 Copying(拷貝)算法?
- 4.5、 Copying(拷貝)算法在年輕代中的具體應用
- 總結
前言
本文進入我們進入 JVM 調優系列 2,GC 如何判斷對象是否為垃圾,這個是面試中的高頻面試題,同時對于 GC 的三色標記算法屬于 GC 算法的核心內容,我們將通過算法的應用原理進行深度剖析并分析存在的問題,由此來得出 GC 的制定機制是什么?這里就不再強調重點了,因為到處都是重點!一、如何判斷一個對象是否為垃圾?
1.1、reference count(引用計數)
查看是否有引用指向該對象,有則說明該對象不是垃圾,反之就是垃圾。
我們通過下圖的引用對象案例來說明。
如上圖所示,我們可以看到一共是存在四個階段。
- 第一階段,有 3 個引用指向該對象,那該對象肯定不是垃圾。
- 第二三階段,部分引用消失,分別各有 2 個和 3 個引用指向該對象,那該對象仍然不是垃圾。
- 第四階段,沒有任何引用再指向該對象,該對象淪為垃圾。這時垃圾回收器就可以將其回收。
1.2、reference count(引用計數)存在的問題
當出現循環引用時,如下圖所示:
我們可以看到,三個對象各自指向循環中的另一個對象,但是沒有其他引用指向這三個對象,那這三個對象就屬于“一堆垃圾”。
那現在我們上面所說的引用計數就不能解決這個該問題,這時我們就需要使用另外一種定位方式——Root Searching(根可達算法或根搜索算法)。
二、Root Searching(根可達算法或根搜索算法)
2.1、Root Searching 釋義
所謂的“根”即是:所有的程序都是從 main 方法來運行,在 main 方法里面 new 出來的對象即為根對象。
例如:在 main 方法里面我們 new 了一個 list 集合,在 list 集合中我們又可以存放若干其他對象,那我們就稱 list 為根對象,我們順著根的數據結構往下走,只要存在引用指向的對象,那該對象就不是垃圾,反之不存在引用的對象,那該對象就是垃圾。
如上圖所示,對象一、二、三、四、五均是存在根對象的引用,對象五、六之間是我們上面所提到的循環引用,對象八不存在引用,故對象六、七、八是垃圾。
2.2、根對象(root)的類型
根對象不僅僅包括我們上面所說的 main 方法里面的對象,屬于根對象的還有以下這些:
- JVM stack
- native method stack
- runtime constant pool
- static references in method area
- Clazz
三、三色標記算法原理與存在的問題
GC Algorithms 到目前為止一共是有三種,我們將一一進行介紹。
- Mark-Sweep(標記清除)
- Copying(拷貝)
- Mark-Compact(標記壓縮或標記整理)
3.1、Mark-Sweep(標記清除)
3.1.1、Mark-Sweep(標記清除)應用原理
如上圖所示,我們將可回收的垃圾對象進行標記定位,進行清除即可。將垃圾位變為可用位。
3.1.2、存在問題-內存碎片化
算法比較簡單,存在缺點,長時間的運行,內存中會存在大量的碎片(碎片化問題)。
何為碎片化?
由上述得知,每一小塊可回收內存均需要標記后單獨清除,在業務量較大,頻繁更新數據的情況下,會有個別的“碎片”長期存在于內存中不去使用,占用資源空間。大量的碎片就會造成查詢效率極其低下,所以我們就需要進行處理。
3.2、Copying(拷貝)
如果我們不想出現碎片化問題,我們就可以考慮使用 Copying(拷貝)算法。
3.2.1、Copying(拷貝)應用原理
如上圖所示,拷貝算法不管內存有多大,直接一分為二,每次使用僅使用內存的一半,在被使用的內存即將用盡時,將可以使用的存活對象拷貝到另一半內存中,將剩下的可回收的垃圾對象進行回收操作。在另一半內存中進行正常操作,如此循環往復。
這種算法每次拷貝完成所有的內存空間都是排列在一起,故不會產生碎片化問題。
3.2.2、存在問題-浪費空間
該算法的優勢即是它的劣勢,每次僅可以使用一般的內存空間進行操作,相當于浪費了一半的內存空間。
3.3、Mark-Compact(標記壓縮或標記整理)
Mark-Compact(標記壓縮)的優勢在于完善了上述兩種算法存在的缺點,既不存在碎片化問題,也不浪費空間。
3.3.1、Mark-Compact(標記壓縮或標記整理)應用原理
把有用的存活對象壓縮到內存空間的最前面,對可回收的垃圾對象進行處理,如上圖所示。
3.3.2、存在的問題-效率過低
由于每次在壓縮之間都需要計算空間,導致回收的效率大大降低。
四、垃圾回收器的制定原則
上述三種標記算法可謂是各有利弊,因此在實際應用中,一個垃圾回收器的制定是綜合了上述三種算法。
4.1、綜合三種算法的 GC
如上圖所示,我們將新誕生的對象存放在新生代里。如果新誕生的對象經歷了數次垃圾回收仍然沒有被回收掉(即每經歷一次垃圾回收,該對象年齡 +1,即 age++),當 age 到達一定數值,將該對象置于老年代中進行特殊處理。
4.2、新生代里面對象的 age 要取值多少?
這個即是我們進行 JVM 調優所需要的自行調整的,根據項目需求來設置。
同時對于年齡的設置,與具體所使用的 GC 息息相關。
- 如果之前沒有對 GC 進行調整或調優的話,默認使用的 GC 為使用的是 PS+PO(Parallel Scavenge+Parallel
Old),默認年齡為 15。 - 如果進行調整之后所使用的 GC 是 CMS,那 age 就是 6。
- 如果使用的 GC 是 G1 的話,則就徹底與 age 無關,因為該 GC 不分代。
4.3、堆內存邏輯分區介紹(適用分代垃圾回收器)
在 4.1 圖中,老年代為 tenured。我們將新生代分為三個部分:伊甸園區和兩個 survivor 區。
- 伊甸園區,即對象誕生的地方,存放所有新生的對象,與在西方中我們人類誕生的地方——伊甸園想對應。
- survivor 區,幸存者區,存放沒有在垃圾回收中被回收的對象,有兩個,通常命名為 s0、s1 或者 s1、s2 等叫法。
我們一般在年輕代中使用的 GC 算法為 Copying(拷貝),老年代中使用的 GC 算法為 Mark-Sweep(標記清除)和 Mark-Compact(標記壓縮或標記整理)。
4.4、為什么年輕代用 Copying(拷貝)算法?
首先我們先考慮 Mark-Sweep(標記清除)和 Mark-Compact(標記壓縮或標記整理),上面我們已經說到,這兩種 GC 算法的缺點分別是:產生碎片化問題、內存回收效率低。
程序產生對象后,該對象很可能會在很短的時間內被回收,根據統計,一次垃圾回收可以回收掉 90% 的對象。在這樣的情況下,使用 Mark-Sweep(標記清除)和 Mark-Compact(標記壓縮或標記整理)效率就太低了,會造成伊甸園區很快爆滿或者大規模碎片化,而新產生的對象產生放進去的效率就會大大降低。
所以在 JVM 設計中,要求年輕代的算法效率是特別高、特別快的。而 Copying(拷貝)算法的效率是最高的,但是浪費了年輕代中至少一半的內存空間。
那我們既要利用好 Copying(拷貝)算法效率高的優勢,又要盡量避免內存浪費的問題,怎么解決?
4.5、 Copying(拷貝)算法在年輕代中的具體應用
第一次垃圾回收:首先將 10% 的幸存對象拷貝到第一個 survivor 中,即 s0 中,然后將整個伊甸園區進行清除。這時所有有用對象都存放在 s0 中。如下圖所示:
第二次垃圾回收:將伊甸園區中有用的對象拷貝到另一個 survivor 中,即 s1 中,再將之前 s0 中的對象(前提是有用)拷貝到 s1 中,對伊甸園區與第一個 s0 進行垃圾回收。這時所有有用的對象存放在 s1 中。如下圖所示:
第三次垃圾回收:再次利用 s0,將之前存活的對象與伊甸園區中產生的新對象存放在 s0 中,對伊甸園區與 s1 進行二垃圾回收。如下圖所示:
第 n 次垃圾回收:如此循環往復利用新生代中的伊甸園區與 survivor 區即可。
總結
在本文中我們通過引用計數和根可達兩種算法來判斷一個對象是否為垃圾,引出在 GC 中的核心——三色標記算法,對于三色標記算法的核心和流程進行了深度剖析,以及其所存在的問題。三色標記算法又為我們引出 GC 的制定原則,GC 對于拷貝算法如何在新生代中運用以提高 JVM 的效率,都是重點內容,這里就不過分強調了。我是白鹿,一個不懈奮斗的程序猿。望本文能對你有所裨益,歡迎大家的一鍵三連!若有其他問題、建議或者補充可以留言在文章下方,感謝大家的支持!