第三章 垃圾收集器與內存分配策略(一)

Java引用

  1. 強引用(Strong Reference)
    只要強引用存在,則不會回收.
static class Person {
        byte[] content = new byte[1024 * 1024];// 1MB
    }

    static class Country {
        byte[] content = new byte[1024 * 1024 * 10];// 10MB
    }

    static void testStrongReference() {
        Person person = new Person();

        System.out.println(person);
        System.gc();
        System.out.println(person);
    }

我們可以看到,即使調用了GC對象仍然存在,輸出

com.citi.yf.referenceTest.Test$Person@15db9742
gc log 省略 ... ... 
com.citi.yf.referenceTest.Test$Person@15db9742
  1. 軟引用(Soft Reference)
    在系統將要發生OOM之前,把軟引用的對象進行回收,如果內存還不夠則OOM.
    如果JVM內存足夠,手動調用System.gc() ,不會回收這部分對象.
    static void testSoftReference() {
        //create 10MB object
        SoftReference<Country> ref = new SoftReference<Country>(new Country());
        System.out.println(ref.get());

        //create 25MB objects, 10+25>30, will trigger GC
        java.util.List<Person> persons = new ArrayList<>();
        for (int i = 0; i < 25; i++) {
            persons.add(new Person());
        }

        System.out.println(ref.get());
    }

run configurations:

-XX:+PrintGCDetails -Xms30m -Xmx30m

我們將jvm內存定在30MB, 測試中先創建了10MB的country, 在接下來的創建25*1MB的persons中,必定會出現GC, 看看是否會回收country.

com.citi.yf.referenceTest.Test$Country@15db9742
gc log 省略 ... ... 
null

最后輸出的country為null,說明已被回收.

  1. 弱引用(Weak Reference)
    GC的時候回收這部分對象.
static void testWeakReference() {
        WeakReference<Person> ref = new WeakReference<Person>(new Person());

        System.out.println(ref.get());
        System.gc();
        System.out.println(ref.get());
    }
com.citi.yf.referenceTest.Test$Person@15db9742
gc log 省略 ... ...
null
  1. 虛引用(Phantom Reference)
    一個對象是否有虛引用不影響其生存時間,且通過虛引用獲得的對象一直是null.
static void testPhantomReference(){
        PhantomReference<Person> ref = new PhantomReference<Test.Person>(new Person(), null);
        
        System.out.println(ref.get());
        System.gc();
        System.out.println(ref.get());
    }
null
gc log 省略 ... ... 
null

Java對象的回收

  1. 判斷對象是否需要回收
  • 引用計數法
    對象添加一個引用計數器,每當有一個地方引用他,計數器+1;引用失效則計數 器-1。計數器=0時就是需要回收之時。
    無法回收互相引用的對象。

  • 可達性分析
    以GC Roots為起始點開始向下搜索,搜索走過的路徑稱為引用鏈,當一個對象到GC Roots沒有任何引用鏈相連時,則證明該對象是不可用的。

    可作為GC Roots的對象:

    • 虛擬機棧中引用的對象
    • 方法區中類靜態屬性引用的對象
    • 方法區中常量引用的對象
    • 本地方法棧中引用的對象
  1. 對象回收流程:

GC Root不可達對象->標記&篩選->二次標記, 如果對象被兩次標記,那么他基本上被真的回收了。 從上面的流程可以看出,對象可以在finalize方法中把自己救活,但是只能自救一次,因為finalize只能執行一次。

篩選:剔除沒有覆蓋 finalize()方法或者執行過finalize()方法的對象。

類的回收

同時滿足:

  • 該類所有的實例已被回收
  • 加載該類的classLoader已被回收
  • 該類對應的java.lang.Class對象沒有在任何地方被引用
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容