強引用(Strong Reference)
就是我們最常見的普通對象引用,只要還有強引用指向一個對象,就能表明對象還“活着”,垃圾收集器不會碰這種對象。對於一個普通的對象,如果沒有其他的引用關係,只要超過了引用的作用域或者顯式地將相應(強)引用賦值爲 null,就是可以被垃圾收集的了。
軟引用(Soft Reference)
實現類爲:SoftReference。只有當JVM認爲內存不足時,纔會試圖回收軟引用指向的對象,JVM會確保在拋出OutOfMemoryError之前,清理軟引用指向的對象。(適合做緩存)通過下面的代碼可以驗證:
import java.lang.ref.SoftReference;
public class SoftReferenceTest {
//-Xms25m -Xmx25m -Xmn20m -XX:+PrintGCDetails
public static void main(String[] args) {
softReference();
}
public static void softReference() {
//申請10M的數據
byte[] referent = new byte[1024*1024*10];
SoftReference<Object> softRerference = new SoftReference<Object>(referent);
referent = null;
//不會回收軟引用的數據,
System.gc();
//軟引用的對象在內存充足的情況下不會回收
if(softRerference.get() != null){
System.out.println("true");
}else{
System.out.println("false");
}
//因爲空間不足,會回收軟引用的數據
byte[] another = new byte[1024*1024*10];
if(softRerference.get() != null){
System.out.println("true");
}else{
System.out.println("false");
}
System.out.println("end");
}
}
弱引用(Weak Reference):
實現類爲:WeakReference。可以用來構建一種沒有特定約束的關係,同樣是緩存實現的選擇(WeekHashMap就是採用弱引用的方式實現的)。JVM一旦發現了某個對象只有弱引用與之關聯,不管當前內存空間足夠與否,都會回收它的內存。下面代碼可以驗證:
import java.lang.ref.WeakReference;
public class WeakReferenceTest {
// -Xms25m -Xmx25m -Xmn20m -XX:+PrintGCDetails
public static void main(String[] args) {
weekReference();
}
public static void weekReference() {
// 申請10M的數據
byte[] referent = new byte[1024 * 1024 * 10];
WeakReference<Object> softRerference = new WeakReference<Object>(referent);
referent = null;
//弱引用的數據,在垃圾回收器線程掃描它所管轄的內存區域的過程中,一旦發現了只具有弱引用的對象,不管當前內存空間足夠與否,都會回收它的內存
System.gc();
// 軟引用的對象在內存充足的情況下不會回收
if (softRerference.get() != null) {
System.out.println("true");
} else {
System.out.println("false");
}
}
}
幻象引用(Phantom Reference)
實現類爲:PhantomReference。提供了一種確保對象被finalize以後,做某些事情的機制。(Java平臺自身的Cleaner機制)如:申請堆外內存時,在JVM堆中會創建一個對應的Cleaner對象,這個Cleaner類繼承了PhantomReference,當DirectByteBuffer對象被回收時,可以執行對應的Cleaner對象的clean方法,做一些後續工作,這裏是釋放之前申請的堆外內存。
引用何時被加到ReferenceQueue隊列裏
在構造軟引用,弱引用和幻象引用的時候,可以傳入一個ReferenceQueue的對象,這個隊列是用來做什麼的呢?當軟引用,弱引用和幻象引用所引用的對象被回收之後,對應的SoftReference,WeakReference,PhantomReference 對象已經不再具有存在的價值,需要一個適當的清除機制,避免大量Reference對象帶來的內存泄漏。而這個隊列就是由JVM將引用對象加入到隊列裏,由JVM將Reference對象清理。加入隊列是由ReferenceHandler這個線程來來做的,代碼如下圖所示:
- tryHandlePending方法的代碼如下:
static boolean tryHandlePending(boolean waitForNotify) {
Reference<Object> r;
Cleaner c;
try {
synchronized (lock) {
if (pending != null) { //pending由JVM進行賦值
r = pending;
// 'instanceof' might throw OutOfMemoryError sometimes
// so do this before un-linking 'r' from the 'pending' chain...
c = r instanceof Cleaner ? (Cleaner) r : null;
// unlink 'r' from 'pending' chain
pending = r.discovered; //將pending的值往下移
r.discovered = null;
} else {
if (waitForNotify) {
lock.wait();
}
return waitForNotify;
}
}
} catch (OutOfMemoryError x) {
Thread.yield();
// retry
return true;
} catch (InterruptedException x) {
// retry
return true;
}
//Cleaner 類型的直接掉用clean對象,不會加入到隊列裏了
if (c != null) {
c.clean();
return true;
}
//這裏將Reference對象加入到隊列裏
ReferenceQueue<? super Object> q = r.queue;
if (q != ReferenceQueue.NULL) q.enqueue(r);
return true;
}
Finalizer引用
Finalizer繼承Reference,Finalizer在我們的系統裏無法被構造(類被定義成package final 類型),Finalizer的實例是一個雙向鏈表的結構,內部有prev與next指針,提供了add與remove方法將對象增加到鏈表與從鏈表中刪除對象。任何類只要實現了Object類裏的finalize方法,JVM在初使化這個對象的時候(調用構造方法的時候),會構造一個Finalizer對象,通過調用Finalizer的register方法,代碼如下:
在構造方法裏,會調用add方法,將Finalizer對象加入到鏈表裏,代碼如下:
,我們分析dump內存的時候,經常能看到 java.lang.ref.Finalizer佔用的內存大小遠遠排在前面,就是因爲系統裏構造了大量的實現了finalize方法的對象。
何時被加入到ReferenceQueue裏
當gc發生的時候,gc算法會判斷對象是不是隻被Finalizer類引用,如果這個類僅僅被Finalizer對象引用的時候,說明這個對象在不久的將來會被回收了現在可以執行它的finalize方法了,於是會將這個Finalizer對象放到Finalizer類的ReferenceQueue裏,但是這個f類對象其實並沒有被回收,因爲Finalizer這個類還對他們持有引用,在gc完成之前,jvm會調用ReferenceQueue裏的lock對象的notify方法(當ReferenceQueue爲空的時候,FinalizerThread線程會調用ReferenceQueue的lock對象的wait方法直到被jvm喚醒)。
何時調用finalize方法
Finalizer類裏定義了FinalizerThread,用於將ReferenceQueue裏的對象取出並執行finalize方法。具體代碼如下:
軟引用的具體回收時機可以參考:https://www.jianshu.com/p/e46158238a77
參考文章:https://time.geekbang.org/column/article/6970
https://www.jianshu.com/p/e46158238a77
https://www.jianshu.com/p/7200da8b043f
https://mp.weixin.qq.com/s/fftHK8gZXHCXWpHxhPQpBg