Java四種引用

Java中提供了一個Reference抽象類,此類定義全部引用對象共有的操做,與垃圾收集器密切配合實現的。主要是爲了決定某些對象的生命週期,有利於JVM進行垃圾回收。而繼承此類的有四種引用,分別是StrongReference(強引用),SoftReference(軟引用),WeakReference(弱引用),PhantomReference(虛引用),強度按照上面的順序依次減弱。下面來看下四種引用的對比。java

類型 調用方式 回收條件 內存泄漏
StrongReference 直接調用 不回收 可能
StrongReference get()方法 視內存狀況回收 不可能
WeakReference get()方法 永遠回收 不可能
PhantomReference 沒法取得 不回收 可能
  • 強引用
Object object = new Object()

上面這段代碼就是一個強引用,是最普通的引用,當內存空間不足, Java 虛擬機寧願拋出 OutOfMemoryError 錯誤,使程序異常終止, 也不會靠隨意回收具備強引用的對象來解決內存不足的問。若是想中斷或者回收強引用,能夠設置引用爲null,如object =null,這樣的話JVM就會在合適的時間,進行垃圾回收。能夠看下下面代碼和運行狀況。緩存

private static void strongTest() {
    printlnMemory("Init");
    // 申請5MB的內存
    byte[] strong = new byte[5 * MB];
    printlnMemory("Use 5MB");
    // 回收
    System.gc();
    printlnMemory("GC after");
    System.out.println("gc strong:" + strong);
    // 設置引用爲null
    strong = null;
    printlnMemory("set null");
    System.out.println("set null strong:" + strong);
    // 回收
    System.gc();
    printlnMemory("null GC after");
    System.out.println("gc strong:" + strong);
}

運行狀況:code

Init:240M(free)/245M(total)

Use 5MB:235M(free)/245M(total)// 使用了5MB內存

GC after:237M(free)/245M(total)// 釋放一些內存
gc strong:[B@7ea987ac

set null:237M(free)/245M(total)// 強引用設置爲null後,內存不變
set null strong:null

null GC after:242M(free)/245M(total)//強引用設置爲null後,回收5MB內存
gc strong:null
  • 軟引用
SoftReference<Object> soft = new SoftReference(new Object());

若一個對象只有軟引用,則當空間不足的時候纔會回收它,能夠用來構建敏感數據的緩存(如網頁緩存、圖片緩存等)。軟引用能夠和一個引用隊列一同使用,當所引用的對象被回收,軟引用便被加入到引用隊列。能夠看下下面代碼和運行狀況。對象

private static void softTest() {
    printlnMemory("Init");
    SoftReference<byte[]> soft = new SoftReference<>(new byte[2000 * MB]);// 申請2000MB的內存
    printlnMemory("Use 2000MB");

    System.gc();// gc回收
    printlnMemory("GC after");
    System.out.println("gc soft:" + soft.get());

    SoftReference<byte[]> soft2 = new SoftReference<>(new byte[2000 * MB]);// 再次申請2000MB的內存
    printlnMemory("use after");
    System.out.println("gc soft:" + soft.get());
}

運行狀況繼承

Init:239M(free)/245M(total)

Use 2000MB:239M(free)/2246M(total)//總內存變大了

GC after:243M(free)/2246M(total) //內存足夠沒有回收
gc soft:[B@2db0f6b2

use after:471M(free)/2474M(total)//內存不夠,自動回收
gc soft:null
  • 弱引用
WeakReference<Object> soft = new WeakReference<>(new Object());

弱引用用來描述非必需對象的,當JVM進行垃圾回收時,不管內存是否充足,都會回收被弱引用關聯的對象。也能夠用來構建敏感數據的緩存,如用於生命週期更短的,對內存更敏感的場景中,好比佔用內存很大的Map,java提供了WeakHashMap。能夠看下下面代碼和運行狀況生命週期

private static void weakTest() {
    printlnMemory("Init");
    WeakReference<byte[]> weak= new WeakReference<>(new byte[10 * MB]);
    printlnMemory("Use 10MB");

    System.gc();
    printlnMemory("GC after");
    System.out.println("gc weak:" + weak.get());
}

運行狀況隊列

Init:239M(free)/245M(total)

Use 10MB:229M(free)/245M(total)

GC after:243M(free)/245M(total)//無論內存是否充足,都進行回收
gc soft:null //weak.get沒法再回去對象
  • 虛引用
ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
PhantomReference<Object> phantom = new PhantomReference<Object>(new Object(), queue);

若一個對象擁有虛引用,則在任什麼時候候均可能被回收。虛引用必須和引用隊列聯合使用,當所引用的對象被回收,虛引用便被加入到引用隊列,主要用來追蹤垃圾回收過程。圖片

private static void phantomTest() {
    printlnMemory("Init");
    byte[] bytes = new byte[5 * MB];
    ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
    PhantomReference<Object> phantom = new PhantomReference<Object>(bytes, queue);
    printlnMemory("Use 5MB");
    System.out.println("phantom : " + phantom);
    System.out.println("phantom.get() : " + phantom.get());
    System.out.println("queue.poll() : " + queue.poll());
    //斷開強引用
    bytes = null;
    System.gc();
    printlnMemory("GC after bytes");
    System.out.println("phantom : " + phantom);
    System.out.println("phantom.get() : " + phantom.get());
    System.out.println("queue.poll() : " + queue.poll());

    //斷開虛引用
    phantom = null;
    System.gc();
    printlnMemory("GC after phantom");
    System.out.println("phantom : " + phantom);
    System.out.println("queue.poll() : " + queue.poll());
}

運行狀況內存

Init:239M(free)/245M(total)

Use 5MB:234M(free)/245M(total)
phantom : java.lang.ref.PhantomReference@2db0f6b2
phantom.get() : null
queue.poll() : null

GC after bytes:238M(free)/245M(total)
phantom : java.lang.ref.PhantomReference@2db0f6b2
phantom.get() : null
queue.poll() : java.lang.ref.PhantomReference@2db0f6b2

GC after phantom:243M(free)/245M(total)
phantom : null
queue.poll() : null
  • ReferenceQueue

顧名思義存放引用的隊列,保存的是Reference對象,其做用在於Reference對象所引用的對象被GC回收時,該Reference對象將會被加入引用隊列中的隊列末尾。rem

經常使用的方法:

  • poll():從隊列中取出一個元素,隊列爲空則返回null
  • remove():從隊列中出對一個元素,若沒有則阻塞至有可出隊元素
  • remove(long timeout):從隊列中出對一個元素,若沒有則阻塞至有可出對元素或阻塞至超過timeout毫秒;

能夠看下下面代碼

byte[] bytes = new byte[5 * MB];
ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
PhantomReference<Object> phantom = new PhantomReference<Object>(bytes, queue);

這段代碼中,對於byte對象有兩種引用類型,一是bytes 的強引用,二是phantom 的虛引用。當bytes 被回收時,phantom 所引用的對象將會被放到queue 的隊列末尾。利用ReferenceQueue能夠清除失去了虛引用對象的引用。

相關文章
相關標籤/搜索