1. 程式人生 > 程式設計 >GC演演算法和種類

GC演演算法和種類

GC的概念

 Garbage Collection 垃圾收集,簡稱GC。Java中GC的物件是堆空間和永久區。
複製程式碼

GC演演算法

引用計數法
  • 概念:引用計數法是老牌的垃圾回收演演算法了,通過物件的引用來計算要不要回收,com、ActionScript3、python 都使用過。
  • 原理:對於一個物件A,只要任何一個物件引用,則A的引用計數器就加1,如果取消引用就減一。如果計數器為零這個物件就是垃圾
  • 缺點:引用和去除引用伴隨著加法減法,影響效能。還有就是很難處理迴圈引用(看下圖)。
  • 優點:即時回收,因為物件知道自己什麼時候沒用
標記清除
  • 概念:標記——清除演演算法是現代垃圾回收演演算法的思想基礎。
  • 原理:這個演演算法將垃圾回收分為了兩個階段,標記階段和清除階段。一種可行的實現是,在標記階段,首先通過根節點,標記所有從根節點開始可達物件。因此未標記的物件就是未被引用的垃圾物件。然後在清除階段,清除未被標記的物件。(看下圖)
  • 缺點:逐漸產生被細化的分塊,不久後就會導致無數的 小分塊散佈在堆的各處。這個記憶體就會被分的七零八落的,這樣在分配物件的時候還得先遍歷那些記憶體塊可以用。
  • 優點:實現起來簡單
標記壓縮
  • 概念:標記-壓縮演演算法適合用於存活物件較多的場合,如老年代。它在標記-清除演演算法的基礎上做了一些優化
  • 原理:和標記清除一樣,先標記但是之後清除操作的時候將所有存活的物件壓縮到記憶體的一端,然後清理這個邊界外的所有空間
  • 缺點:清除演演算法中,清除階段也要搜尋整個堆,不過搜尋 1 次就夠了。但 GC 標記 - 壓縮演演算法要搜尋 3 次,這樣就要花費約 3 倍的時間,這是一個相當巨大的缺陷,特別是堆越 大,所消耗的成本也就越大(看圖)
  • 優點:堆利用效率高。 而且 GC 標記 - 壓縮演演算法不會出現 GC 複製演演算法那樣只能利用半個堆的情況
複製演演算法
  • 概念:在標記清除的的基礎上改進

  • 原理:將原有的記憶體空間分為兩塊,每次只使用其中一塊,在垃圾回收時,將正在使用的記憶體中的存活物件複製到未使用的記憶體塊中,之後,清除正在使用的記憶體塊中的所有物件,交換兩個記憶體的角色,完成垃圾回收

  • 缺點:看圖

  • 優點:

    1、優秀的吞吐量: GC 標記 - 清除演演算法消耗的吞吐量是搜尋活動物件(標記階段)所花費的時間和搜尋整體 堆(清除階段)所花費的時間之和。  
     另一方面,因為 GC 複製演演算法只搜尋並複製活動物件,所以跟一般的 GC 標記 - 清除算 法相比,它能在較短時間內完成 GC。也就是說,其吞吐量優秀。
    
    2、高速分配:GC 複製演演算法不使用空閒連結串列。這是因為分塊是一個連續的記憶體空間。比起 GC 標記 - 清除演演算法和引用計數法等使用空閒連結串列的分配,GC 複製演演算法明顯快得多。
    
    3、不會發生碎片化
    複製程式碼
GC演演算法總結

總結的時候我覺得還是引入一下分代思想比較清晰

依據物件的存活週期進行分類,短命物件歸為新生代,長命物件歸為老年代。

根據不同代的特點,選取合適的收集演演算法

  • 少量物件存活,適合複製演演算法
  • 大量物件存活,適合標記清理或者標記壓縮

可觸及性

問:GC演演算法中 怎麼判斷這個物件是否是垃圾物件,是根據可觸及性來判斷的,那麼什麼是可觸及性呢。

答:

  • 棧中引用的物件
  • 方法區中靜態成員或者常量引用的物件(全域性物件)
  • JNI方法棧中引用物件

還有一個概念也不能判定這個物件是垃圾物件就是 --可復活的

  一旦所有引用被釋放,就是可復活狀態,因為在finalize()中可能復活該物件
複製程式碼

不過這個一般不使用:

 避免使用finalize(),操作不慎可能導致錯誤。
 優先順序低,何時被呼叫, 不確定
 何時發生GC不確定
 可以使用try-catch-finally來替代它經驗:避免使用finalize(),操作不慎可能導致錯誤。 
 優先順序低,何時被呼叫, 不確定
 何時發生GC不確定
 可以使用try-catch-finally來替代它
複製程式碼

這裡你可以程式碼試下

public class CanReliveObj {
   public static CanReliveObj obj;
   @Override
   protected void finalize() throws Throwable {
       super.finalize();
       System.out.println("CanReliveObj finalize called");
       obj=this;
   }
   @Override
   public String toString(){
       return "I am CanReliveObj";
   }


public static void main(String[] args) throws
    InterruptedException{
obj=new CanReliveObj();
obj=null;   //可復活
System.gc();
Thread.sleep(1000);
if(obj==null){
   System.out.println("obj 是 null");
}else{
   System.out.println("obj 可用");
}
System.out.println("第二次gc");
obj=null;    //不可復活
System.gc();
Thread.sleep(1000);
if(obj==null){
System.out.println("obj 是 null");
}else{
System.out.println("obj 可用");
}
}
複製程式碼

Stop-The-World

詳情點選 juejin.im/post/5d5a1b…

Java中一種全域性暫停的現象,全域性停頓,所有Java程式碼停止,native程式碼可以執行,但不能和JVM互動,多半由於GC引起 Dump執行緒、 死鎖檢查、堆Dump。

GC時為什麼會有全域性停頓? 類比在聚會時打掃房間,聚會時很亂,又有新的垃圾產生,房間永遠打掃不乾淨,只有讓大家停止活動了,才能將房間打掃乾淨。

危害:長時間服務停止,沒有響應,遇到HA系統,可能引起主備切換,嚴重危害生產環境。