生存還是死亡
對象是否需要被垃圾收集器回收主要有兩種方式:引用計數法
和可達性分析算法
引用計數法
給對象添加一個引用計數器,每當有一個地方引用他的時候,計數器的數值就+1,當引用失效時,計數器就-1;任何時候計數器的數值都為0的對象時不可能再被使用的。
客觀的來說,引用計數法實現簡單,判定效率高,但是無法解決對象的循環引用的問題。所以現在的虛擬機很少使用這種算法辣判斷對象是否存活。
可達性分析算法
基本思路就是:通過一系列稱為GC Roots
的對象作為起始點,從這些起始點開始向下搜索,搜索所搜過的路徑稱為引用鏈Reference Chain
,當一個對象到GC Roots沒有任何引用鏈相連接時,則證明此對象時不可用的。如下圖所示:4,5就被判定為需要回收的對象
GC Roots
在虛擬機中可作為GC Roots的對象包括以下幾種:
- 虛擬機棧中引用的對象
- 方法區中類靜態屬性引用的對象
- 方法區常量引用的對象
- 本地方法棧引用的對象
GC算法
在被可達性分析算法
判定為需要被回收的對象時,需要進行垃圾回收操作,垃圾回收算法有以下幾種:
標記清除(mark-sweep)
最基礎的收集算法,如同 它的名字一樣,算法分為標記
和清除
兩個階段:首先標記出需要被回收的對象,在標記完成后統一回收所有被標記的對象,它的標記過程其實就是上面所講的可達性分析算法
被標記為沒有GC roots的引用鏈。過程如下:
不足
- 效率問題,標記和清除兩個過程的效率都不高
- 空間問題,標記清除和產生大量不連續的內存碎片,內存碎片過多容易導致以后在程序運行的過程中需要分配大對象時,無法找到足夠的連續內存而不得不提前觸發一次垃圾收集動作。
適用場景
適用於老年代。因為老年代回收的幾率小且不頻繁能減少內存碎片
復制 (coping)
為了解決標記-清除得到效率問題,復制
算法就出現了;它的原理是將可用內存容量划分為兩個大小相等的內存塊,每次只使用其中的一個,當這個的容量用完了。就將還存活的對象復制到另外一塊上面去,然后再把已使用過的內存空間全部清理掉。這樣使得每次都是對整個半區進行內存回收,內存分配就不用考考慮碎片等復雜情況
研究表明,新生代中的對象98%都是朝生夕死的,所以按照並不需要1:1來划分空間。而是將內存分為一塊較大的Eden空間和兩個較小的Survivor空間
,每次使用Eden和其中一款Survivor。一般比例是Eden : Survivor = 8:1
不足
- 空間利用率只有50%,但是經過適當的調整Eden和Survivor的比例能達到70%
- 復制收集算法在對象存活率較高時就要進行較多的復制操作,效率將會變低
適用場景
使用了對象存活率較低的內存空間,如Eden區。
標記整理
是標記-清除算法的升級版本。標記過程任然與標記-清除算法一樣,但是后續的處理步驟不是直接對可回收對象進行清理,而是讓所有存活對象都向一端移動,然后直接清理掉端邊界意外的內存、標記-整理算法示意圖如下: