JVM垃圾回收(GC)
- 搜尋算法-引用計數器算法(廢棄)
- 搜尋算法-根搜尋算法(使用)
-
- 回收算法-标記—清除算法(Mark-Sweep)
- 回收算法-标記—整理算法(Mark-Compact)
- 回收算法-複制算法(Copying)
- 回收算法-分代收集算法(Generational Collection)
- 總結
GC(Garbage Collector)在回收對象前首先必須發現那些無用的對象,如何去發現定位這些無用的對象?常用的搜尋算法如下:
搜尋算法-引用計數器算法(廢棄)
引用計數器算法是給每個對象設定一個計數器, 當有地方引用這個對象的時候,計數器+1,當引用失效的時候,計數器-1,當計數器為0的時候,JVM就認為對象不再被使用,是"垃圾”了。
優點:引用計數器實作簡單,效率高;
缺點:不能解決循環引用問題(A 對象引用B對象,B對象又引用A對象,但是A、B對象已不被任何其他對象引用),同時每次計數器的增加和減少都帶來了很多額外的開銷。
是以在JDK1.1之後,這個算法已經不再使用了。
搜尋算法-根搜尋算法(使用)
根搜尋算法是通過一些“GC Roots”對象作為起點,從這些節點開始往下搜尋,搜尋通過的路徑成為引用鍊(Reference Chain),當一個對象沒有被GC Roots的引用鍊連接配接的時候,說明這個對象是不可用的。
GC Roots對象包括:
- 虛拟機棧(棧幀中的本地變量表)中的引用的對象;
- 方法區域中的類靜态屬性引用的對象;
- 方法區域中常量引用的對象;
- 本地方法棧中JNI(Native方法)的引用的對象。
通過上面的算法搜尋到無用對象之後,就是回收過程,回收算法如下:
回收算法-标記—清除算法(Mark-Sweep)
标記—清除算法包括兩個階段:“标記”和“清除”。在标記階段,确定所有要回收的對象,并做标記。清除階段緊随标記階段,将标記階段确定不可用的對象清除。
标記—清除算法是基礎的收集算法,标記和清除階段的效率不高,而且清除後回産生大量的不連續空間,這樣當程式需要配置設定大記憶體對象時,可能無法找到足夠的連續空間。
優點:不需要額外的空間;
缺點:兩次掃描,嚴重浪費時間,會産生記憶體碎片。
回收算法-标記—整理算法(Mark-Compact)
标記—整理算法和标記—清除算法一樣,但是标記—整理算法不是把存活對象複制到另一塊記憶體,而是把存活對象往記憶體的一端移動,然後直接回收邊界以外的記憶體。标記—整理算法提高了記憶體的使用率,并且它适合在收集對象存活時間較長的老年代。
回收算法-複制算法(Copying)
複制算法是把記憶體分成大小相等的兩塊,每次使用其中一塊,當垃圾回收的時候,把存活的對象複制到另一塊上,然後把這塊記憶體整個清理掉。
複制算法實作簡單,運作效率高,但是由于每次隻能使用其中的一半,造成記憶體的使用率不高。
現在的JVM用複制方法收集新生代,由于新生代中大部分對象(98%)都是朝生夕死的,是以兩塊記憶體的比例不是1:1(大概是8:1)。
好處:沒有記憶體碎片;
壞處:浪費了記憶體空間==>多了一半空間永遠是空to。 假設對象100%存活(極端情況)
複制算法最佳使用場景:對象存活度較低的時候==>新生區
回收算法-分代收集算法(Generational Collection)
分代收集是根據對象的存活時間把記憶體分為新生代和老年代,根據各個代對象的存活特點,每個代采用不同的垃圾回收算法。新生代采用複制算法,老年代采用标記—整理算法。垃圾算法的實作涉及大量的程式細節,而且不同的虛拟機平台實作的方法也各不相同。
總結
-
記憶體效率:複制算法 > 标記清除算法 > 标記壓縮算法(時間複 雜度)
記憶體整齊度:複制算法 = 标記壓縮算法 > 标記清除算法
記憶體使用率:标記壓縮算法 = 标記清除算法 > 複制算法
-
年輕代:存活率低;複制算法。
老年代:區域大、存活率高;标記清除(記憶體碎片不是太多) +标記壓縮混合實作。
- JVM在進行GC時,并不是對三個區域統一回收。大部分時候,回收都是新生代
- 新生代
- 幸存區(form,to)
- 老年區
- GC兩種類:輕GC (普通的GC), 重GC (全局GC)