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
顧名思義存放引用的隊列,保存的是Reference對象,其做用在於Reference對象所引用的對象被GC回收時,該Reference對象將會被加入引用隊列中的隊列末尾。rem
經常使用的方法:
能夠看下下面代碼
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能夠清除失去了虛引用對象的引用。