1. 程式人生 > >菜鳥學習JVM——垃圾回收演算法

菜鳥學習JVM——垃圾回收演算法

Java垃圾回收演算法

所有的垃圾回收演算法都是為了解決三個問題:

  • 哪些記憶體需要回收
  • 什麼時候回收
  • 怎麼回收

引用計數法(Reference Counting)

引用計數法原理很簡單,給每個物件分配一個計數器,當被引用時就加一,引用失效就減一。計數器為零時,則說明該物件不可能再被使用。引用計數法效率不錯,大部分情況下是個不錯的演算法,但它有一個非常明顯的缺點,就是無法回收互相引用的物件,從而引起記憶體洩露。這也是主流Java虛擬機器沒有選擇它的一個重要因素。

![引用計數法](https://img-blog.csdn.net/20160519173738020)
上圖中很明顯A和B已經不其他物件引用了,但他們互相引用,彼此的計數器都不為零,用引用計數法無法將其回收,從而造成了記憶體洩露。 ## **標記-清除法(Mark-Sweep)** 標記-清除演算法是現代垃圾回收演算法的思想基礎,後面提到的所有演算法都是基於該演算法的思想。從名字就能看出,該演算法分兩個階段進行——“標記”和“清除”。首先通過根物件標記所有可達的物件,然後清除所有未被標記的不可達物件。該演算法有一個比較大的缺點,就是容易產生記憶體碎片。過多的記憶體碎片對於大物件的記憶體分配,效率非常低。回收過程如下圖:
![回收前](https://img-blog.csdn.net/20160519180218860) 回收前 `白色表示空閒,灰色表示存活物件,黑色表示垃圾物件`
![回收後](https://img-blog.csdn.net/20160519180425907) 回收後
> **什麼是可達?** > > 簡單的說,就是如果能通過一條路徑找到該物件,那麼就將該物件稱為可達物件。 > 專業點的說法就是,通過一系列根物件(GC Roots)作為起點,向下搜尋,搜尋所有的引用路徑——引用鏈(Reference Chain),只要根物件和該物件相連,那麼該物件就為可達物件,反之,則為不可達的物件,則證明此物件是不可用的,需要被回收。

複製演算法(Copying)

複製演算法基於標記-清除演算法並對其產生過多記憶體碎片的缺點進行了優化。複製演算法將記憶體空間分成兩等份(如下圖的A和B),每次只使用其中的一塊,當垃圾回收的時候,將A中的可達物件複製到B中,然後清空A中的所有物件。這樣就避免了產生記憶體碎片的情況,但這種演算法的缺點也是顯而易見的,那就是太浪費空間。

![回收前](https://img-blog.csdn.net/20160519180218860) A
![回收後](https://img-blog.csdn.net/20160519234751223) B
## **標記-壓縮法(Mark-Compact)** 光從名字上就能看出,該演算法繼承自標記-清除演算法。該演算法在標記和清除之間又加了一個操作——壓縮。首先將標記所有可達物件,然後將所有可達物件壓縮(或者叫移動)到記憶體的一端,最後將邊界以外的空間全部清空。這樣既避免了產生記憶體碎片,又不需要空出一塊記憶體空間,一舉兩得。
![回收前](https://img-blog.csdn.net/20160519180218860) 回收前
![回收後](https://img-blog.csdn.net/20160519234751223) 回收後
>跟上面不同的是:這是同一塊記憶體回收前和回收後的不同狀態,而上面是A、B兩塊不同的記憶體。

總結(Summary)

JVM中採用的並不是某一種回收演算法,而是多種演算法組合使用,因為任何一種演算法都不是完美的,都有自身的優缺點,有自己適用的場景。需要把他們放到合適的地方,這樣才能各盡其能,達到一個最好的效果。