強引用(Strong Reference)
??就是我們最常見的普通對象引用,只要還有強引用指向一個對象,就能表明對象還“活著”,垃圾收集器不會碰這種對象。對于一個普通的對象,如果沒有其他的引用關(guān)系,只要超過了引用的作用域或者顯式地將相應(強)引用賦值為 null,就是可以被垃圾收集的了。
軟引用(Soft Reference)
?? 實現(xiàn)類為:SoftReference。只有當JVM認為內(nèi)存不足時,才會試圖回收軟引用指向的對象,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的數(shù)據(jù)
byte[] referent = new byte[1024*1024*10];
SoftReference<Object> softRerference = new SoftReference<Object>(referent);
referent = null;
//不會回收軟引用的數(shù)據(jù),
System.gc();
//軟引用的對象在內(nèi)存充足的情況下不會回收
if(softRerference.get() != null){
System.out.println("true");
}else{
System.out.println("false");
}
//因為空間不足,會回收軟引用的數(shù)據(jù)
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):
?? 實現(xiàn)類為:WeakReference。可以用來構(gòu)建一種沒有特定約束的關(guān)系,同樣是緩存實現(xiàn)的選擇(WeekHashMap就是采用弱引用的方式實現(xiàn)的)。JVM一旦發(fā)現(xiàn)了某個對象只有弱引用與之關(guān)聯(lián),不管當前內(nèi)存空間足夠與否,都會回收它的內(nèi)存。下面代碼可以驗證:
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的數(shù)據(jù)
byte[] referent = new byte[1024 * 1024 * 10];
WeakReference<Object> softRerference = new WeakReference<Object>(referent);
referent = null;
//弱引用的數(shù)據(jù),在垃圾回收器線程掃描它所管轄的內(nèi)存區(qū)域的過程中,一旦發(fā)現(xiàn)了只具有弱引用的對象,不管當前內(nèi)存空間足夠與否,都會回收它的內(nèi)存
System.gc();
// 軟引用的對象在內(nèi)存充足的情況下不會回收
if (softRerference.get() != null) {
System.out.println("true");
} else {
System.out.println("false");
}
}
}
幻象引用(Phantom Reference)
??實現(xiàn)類為:PhantomReference。提供了一種確保對象被finalize以后,做某些事情的機制。(Java平臺自身的Cleaner機制)如:申請堆外內(nèi)存時,在JVM堆中會創(chuàng)建一個對應的Cleaner對象,這個Cleaner類繼承了PhantomReference,當DirectByteBuffer對象被回收時,可以執(zhí)行對應的Cleaner對象的clean方法,做一些后續(xù)工作,這里是釋放之前申請的堆外內(nèi)存。
引用何時被加到ReferenceQueue隊列里
??在構(gòu)造軟引用,弱引用和幻象引用的時候,可以傳入一個ReferenceQueue的對象,這個隊列是用來做什么的呢?當軟引用,弱引用和幻象引用所引用的對象被回收之后,對應的SoftReference,WeakReference,PhantomReference 對象已經(jīng)不再具有存在的價值,需要一個適當?shù)那宄龣C制,避免大量Reference對象帶來的內(nèi)存泄漏。而這個隊列就是由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,F(xiàn)inalizer在我們的系統(tǒng)里無法被構(gòu)造(類被定義成package final 類型),F(xiàn)inalizer的實例是一個雙向鏈表的結(jié)構(gòu),內(nèi)部有prev與next指針,提供了add與remove方法將對象增加到鏈表與從鏈表中刪除對象。任何類只要實現(xiàn)了Object類里的finalize方法,JVM在初使化這個對象的時候(調(diào)用構(gòu)造方法的時候),會構(gòu)造一個Finalizer對象,通過調(diào)用Finalizer的register方法,代碼如下:
在構(gòu)造方法里,會調(diào)用add方法,將Finalizer對象加入到鏈表里,代碼如下:
何時被加入到ReferenceQueue里
??當gc發(fā)生的時候,gc算法會判斷對象是不是只被Finalizer類引用,如果這個類僅僅被Finalizer對象引用的時候,說明這個對象在不久的將來會被回收了現(xiàn)在可以執(zhí)行它的finalize方法了,于是會將這個Finalizer對象放到Finalizer類的ReferenceQueue里,但是這個f類對象其實并沒有被回收,因為Finalizer這個類還對他們持有引用,在gc完成之前,jvm會調(diào)用ReferenceQueue里的lock對象的notify方法(當ReferenceQueue為空的時候,F(xiàn)inalizerThread線程會調(diào)用ReferenceQueue的lock對象的wait方法直到被jvm喚醒)。
何時調(diào)用finalize方法
??Finalizer類里定義了FinalizerThread,用于將ReferenceQueue里的對象取出并執(zhí)行finalize方法。具體代碼如下:
軟引用的具體回收時機可以參考:http://www.lxweimin.com/p/e46158238a77
參考文章:https://time.geekbang.org/column/article/6970
http://www.lxweimin.com/p/e46158238a77
http://www.lxweimin.com/p/7200da8b043f
https://mp.weixin.qq.com/s/fftHK8gZXHCXWpHxhPQpBg