背景
面試官:什么是強(qiáng)引用歇式、軟引用、弱引用胡野、虛引用材失?
我說
java中為了控制對象的生命周期,在jdk1.2之后引入了強(qiáng)引用硫豆、軟引用龙巨、弱引用、虛引用來靈活地控制對象的生命周期
熊响。
強(qiáng)引用
強(qiáng)引用就是我們平時(shí)直接new出來的對象旨别,舉個栗子:Object strongReference = new Object();
所謂強(qiáng)引用,就是jvm寧愿拋出OOM也不愿意回收的對象
(當(dāng)然是必須先是可達(dá)對象)汗茄,所以強(qiáng)引用是造成OOM的主要原因之一
軟引用
軟引用就是被SoftReference
修飾的對象秸弛,舉個栗子:SoftReference softObj = new SoftReference(new Object())
;
它會在發(fā)生gc的時(shí)候且內(nèi)存不足時(shí)進(jìn)行回收(回收的是引用的內(nèi)容,比如栗子中的new Object),所以軟引用適合用作緩存
胆屿,在內(nèi)存充足的時(shí)候提示程序查詢效率奥喻,內(nèi)存不足時(shí)回收確保系統(tǒng)運(yùn)行正常
弱引用
弱引用就是被WeakReference
修飾的對象,舉個栗子:WeakReference weakR = new WeakReference<>(new Object())
;
它會在gc的時(shí)候被回收非迹,而不關(guān)心內(nèi)存是否充足
可以看到环鲤,強(qiáng)引用與軟引用在發(fā)生gc的時(shí)候都未被回收(軟引用由于不滿足內(nèi)存不足的條件),但是弱引用卻被回收了憎兽。像我們經(jīng)常用到的ThreadLocal就用到了弱引用冷离,用來防止內(nèi)存泄漏
虛引用
虛引用不同于其他三種引用,虛是形同虛設(shè)的虛
纯命,相當(dāng)于沒有引用西剥,在任何時(shí)候都可能被回收。它不能單獨(dú)使用亿汞,也不能通過它訪問對象瞭空,并且必須和引用隊(duì)列(Reference queue)配合使用。
虛引用的主要作用是跟蹤對象被垃圾回收的狀態(tài)疗我。僅僅是提供了一種確保對象被 finalize以后,做某些事情的機(jī)制咆畏。
PhantomReference的get方法總是返回null,因此無法訪問對應(yīng)的引用對象。
使用它的意義在于說明一個對象已經(jīng)進(jìn)入 finalization階段,可以被回收,用來實(shí)現(xiàn)比 finalization機(jī)制更靈活的回收操作 換句話說,設(shè)置虛引用關(guān)聯(lián)的唯一目的,就是在這個對象被收集器回收的時(shí)候收到一個系統(tǒng)通知或者后續(xù)添加進(jìn)一步的處理吴裤;
-
虛引用用來管理堆外內(nèi)存
Reference queue引用隊(duì)列
對象在被回收之前要被引用隊(duì)列保存一下旧找。GC之前對象不放在隊(duì)列中,GC之后才對象放入隊(duì)列中麦牺。
【通過開啟線程監(jiān)聽該引用隊(duì)列的變化情況】就可以在對象被回收時(shí)采取相應(yīng)的動作钮蛛。 由于虛引用的唯一目的就是能在這個對象被垃圾收集器回收時(shí)能收到系統(tǒng)通知,因而創(chuàng)建虛引用時(shí)必須要關(guān)聯(lián)一個引用隊(duì)列剖膳,而軟引用和弱引用則不是必須的魏颓。 這里所謂的收到系統(tǒng)通知其實(shí)還是通過開啟線程監(jiān)聽該引用隊(duì)列的變化情況來實(shí)現(xiàn)的。
這里還需要強(qiáng)調(diào)的是吱晒, 對于軟引用和弱引用琼开,當(dāng)執(zhí)行第一次垃圾回收時(shí),就會將軟引用或弱引用對象添加到其關(guān)聯(lián)的引用隊(duì)列中枕荞,然后其finalize函數(shù)才會被執(zhí)行(如果沒復(fù)寫則不會被執(zhí)行)柜候; 而對于虛引用,如果被引用對象沒有復(fù)寫finalize方法躏精,則是在第一垃圾回收將該類銷毀之后渣刷,才會將虛擬引用對象添加到引用隊(duì)列,如果被引用對象復(fù)寫了finalize方法矗烛,則是當(dāng)執(zhí)行完第二次垃圾回收之后辅柴,才會將虛引用對象添加到其關(guān)聯(lián)的引用隊(duì)列
一個對象的finalize()方法只會被調(diào)用一次箩溃,而且finalize()被調(diào)用不意味著gc會立即回收該對象,所以有可能調(diào)用finalize()后碌嘀,該對象又不需要被回收了涣旨,然后到了真正要被回收的時(shí)候,因?yàn)榍懊嬲{(diào)用過一次股冗,所以不會調(diào)用finalize()霹陡,產(chǎn)生問題,所以,推薦不要使用finalize()方法
class User{
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("我要被GC干了止状!");
}
}
public static void main(String[] args) throws Exception {
User user=new User();
ReferenceQueue<User> queue=new ReferenceQueue();
PhantomReference prf=new PhantomReference(user,queue);
//啟動一個線程監(jiān)控引用隊(duì)列的變化
new Thread(()->{
for(;;){
final Reference<? extends User> u = queue.poll();
if (u!=null){
System.out.println("有對象被加入到了引用隊(duì)列了烹棉!"+u);
}
}
}).start();
user=null;
//GC之前引用隊(duì)列為空
System.out.println("GC之前"+queue.poll());
System.gc();
Thread.sleep(100);
//GC之后引用隊(duì)列才將對象放入
System.out.println("第一次GC之后"+queue.poll());
System.gc();
Thread.sleep(100);
System.out.println("第二次GC之后"+queue.poll());
}
結(jié)果:
GC之前null
我要被GC干了!
第一次GC之后null
有對象被加入到了引用隊(duì)列了怯疤!java.lang.ref.PhantomReference@549763fd
第二次GC之后java.lang.ref.PhantomReference@5aaa6d82