1. 程式人生 > 實用技巧 >強引用、軟引用、弱引用、虛引用及方法區的垃圾回收策略

強引用、軟引用、弱引用、虛引用及方法區的垃圾回收策略

以下按照引用強弱關係進行排序:

  • 強引用

記憶體溢位都不會回收。

  • 軟引用
public class MyDataTest {
    public static void main(String[] args) {
        //SoftReference<String[]> data=new SoftReference<>(new String[1024*1024*500]);
        String[] data=new String[1024*1024*500];
        System.out.println(data);
        MyDataTest.drainMemory();
        System.out.println(data);
    }

    public static void drainMemory(){
        int[] str=new int[1024*1024*500];
    }

public class MyDataTest {
    public static void main(String[] args) {
        SoftReference<String[]> data=new SoftReference<>(new String[1024*1024*500]);
        System.out.println(data.get());
        MyDataTest.drainMemory();
        System.out.println(data.get());
    }

    public static void drainMemory(){
        int[] str=new int[1024*1024*500];
    }
}

記憶體不足時回收軟引用物件,如果記憶體還是不足,才會丟擲記憶體溢位異常。

使用場景:像這種如果記憶體充足,GC時就保留,記憶體不夠,GC再來收集的功能很適合用在==快取==的引用場景中。在使用快取時有一個原則,如果快取中有就從快取獲取,如果沒有就從資料庫中獲取,快取的存在是為了加快計算速度,如果因為快取導致了記憶體不足進而整個程式崩潰,那就得不償失了。

  • 弱引用
public class MyDataTest {
    public static void main(String[] args) {
        MyDataTest.weakRef();
    }

    
public static void weakRef() { WeakReference<MyData> data=new WeakReference<>(new MyData()); System.gc(); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(data.get()); } }
public class MyData {
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("finalize");
}
}

GC即回收。

使用場景:ThreadLocal等,防止記憶體洩漏。

  • 虛引用

一個物件是否有虛引用的存在,完全不會對其生存時間構成影響,也無法通過虛引用來獲取一個物件的例項。為一個物件設定虛引用關聯的唯一目的就是能在這個物件被收集器回收時收到一個系統通知。

  • finalize方法

宣告一個物件的滅亡需要經歷兩次被標記的過程:如果物件在進行可達性分析時發現其不在與GC Roots相連線的引用鏈上,則會被進行第一次標記且進行一次篩選,篩選的條件時是否有必要執行finalize()方法,如果已執行過或物件沒有覆寫finalize(),則認為“沒有必要執行”。如果物件被判定為有必要執行,則會將物件加入F-Queue中,並在稍後由一個由虛擬機器自動建立的、低優先順序的Finalizer執行緒去執行它。這裡所謂的執行是虛擬機器會觸發這個方法但不承諾會等待它執行結束。因為如果一個物件在finalize()中執行緩慢或者發生了死迴圈,將會導致F-Queue佇列中的其他物件處於永久等待狀態,甚至導致整個記憶體回收系統崩潰。稍後GC將對F-Queue中的物件進行第二次小規模的標記,如果物件要在finalize()中成功拯救自己--只要重新與引用鏈上的任何一個物件建立關聯即可,那在二次標記時它將被移出“即將回收的集合”。反之,則被回收。

上述程式碼中第一次逃脫成功、第二次卻失敗了,因為finalize()方法只會被執行一次,第二次自動失效。

  • 回收方法區

在gc過程中,對永久代的垃圾回收主要包含兩部分內容:廢棄常量和無用的類。

常量:例如一個字串“abc”已經進入字串常量池但是當前沒有一個String引用常量池中的“abc”,則回收它。

類:類的回收條件比較苛刻,包含以下三點:Java堆中不存在該類的任何例項,均已被回收;載入該類的ClassLoader已經被回收;該類對應的java.lang.Class物件沒有在任何地方被引用,無法通過反射訪問該類的方法。

以上!!!