文章很長(zhǎng)文末有福利
1. 強(qiáng)引用(StrongReference)
我們平常使用new操作符來創(chuàng)建的對(duì)象就是強(qiáng)引用對(duì)象堂氯,只要有一個(gè)引用存在缺谴,垃圾回收器永遠(yuǎn)不可能回收具有強(qiáng)引用的對(duì)象坑鱼。
Object obj=new Object();
注意:
強(qiáng)引用的對(duì)象并不是永遠(yuǎn)不會(huì)被回收滓走,需要把obj值為null,或者超出對(duì)象的生命周期之后嗓化,GC就有機(jī)會(huì)去回收它棠涮,具體什么時(shí)候回收要看GC。還有刺覆,這里的StrongReference只是一個(gè)對(duì)強(qiáng)引用的稱呼严肪,在java中并沒有對(duì)應(yīng)的實(shí)體類。
2. 軟引用(SoftReference)
軟引用是用來描述一些還有用但并非必須的對(duì)象谦屑。當(dāng)內(nèi)存充足時(shí)驳糯,垃圾回收器不會(huì)清理具有軟引用的對(duì)象,只有當(dāng)內(nèi)存不足時(shí)垃圾回收器才會(huì)去清理這些對(duì)象氢橙,如果清理完軟引用的對(duì)象后內(nèi)存還是不足才會(huì)拋出異常酝枢。軟引用在java中也是一個(gè)對(duì)象,對(duì)應(yīng)的實(shí)體類是SoftReference案例:
這個(gè)案例我們事先把最大堆內(nèi)存改為了24M
-Xmx24M
/**? * 軟引用demo? * SoftReference? * 1.當(dāng)內(nèi)存不足的時(shí)悍手,JVM就會(huì)把軟引用對(duì)象進(jìn)行回收? * 2.如果回收后還是沒有足夠的內(nèi)存帘睦,才會(huì)拋出內(nèi)存溢出異常? */publicstaticvoidmain(String[]args)throwsInterruptedException{SoftReference<byte[]>s=newSoftReference<>(newbyte[1024*1024*10]);//10mSystem.out.println(s.get());System.gc();//啟動(dòng)GCThread.sleep(500);System.out.println(s.get());//再創(chuàng)建一個(gè)數(shù)組,堆中存不下的時(shí)候坦康,垃圾回收器工作//先回收一次竣付,如果第一次回收后內(nèi)存還是不夠//則再清理第二次,這一次會(huì)把軟引用對(duì)象清除byte[]b=newbyte[1024*1024*15];//15mSystem.out.println(s.get());//null}
控制臺(tái)打印結(jié)果
[B@2a139a55
[B@2a139a55
null
此外滞欠,還可以通過以下JVM參數(shù)來打印GC日志
-XX:+PrintGC//打印簡(jiǎn)單的GC日志-XX:+PrintGCDetails//打印詳細(xì)的GC日志
通過控制臺(tái)的打印結(jié)果我們得出結(jié)論:內(nèi)存充足的情況下古胆,具有軟引用的對(duì)象不會(huì)被垃圾回收器回收,當(dāng)再次創(chuàng)建了新的對(duì)象筛璧,結(jié)果導(dǎo)致堆內(nèi)存不足時(shí)就會(huì)啟動(dòng)第一次GC逸绎,這一次不會(huì)回收軟引用關(guān)聯(lián)的對(duì)象,但是當(dāng)?shù)谝淮吻謇碇蟀l(fā)現(xiàn)內(nèi)存還是不夠夭谤,則會(huì)再啟動(dòng)第二次GC棺牧,這一次GC才會(huì)清理掉軟引用關(guān)聯(lián)的對(duì)象。
由于沮翔,在JAVA中軟引用也是一個(gè)類陨帆,我們需要軟引用需要?jiǎng)?chuàng)建軟引用類實(shí)例,我們?cè)谏厦姘咐胁墒矗兞縮的引用指向的是new SoftReference()這個(gè)實(shí)例對(duì)象疲牵,屬于強(qiáng)引用關(guān)系,而在這個(gè)實(shí)例對(duì)象的里面又去引用了我們new出來的byte數(shù)組實(shí)例榆鼠,這個(gè)引用是軟引用關(guān)系纲爸。
SoftReference<byte[]>s=newSoftReference<>(newbyte[1024*1024*10]);
關(guān)系圖如下:
軟引用非常適合用在緩存中,假如用戶訪問的系統(tǒng)中需要加載很多圖片妆够,內(nèi)存夠用的時(shí)候可以緩存很多圖片识啦,假如內(nèi)存不夠用了,再把圖片先回收掉也無妨神妹,下次需要的時(shí)候再加載一次即可颓哮。
3. 弱引用(WeakReference)
無論內(nèi)存夠不夠,只要垃圾回收器啟動(dòng)鸵荠,弱引用關(guān)聯(lián)的對(duì)象肯定被回收冕茅。
弱引用對(duì)象的實(shí)體類是WeakReference。
案例:
/**? * 弱引用demo? * WeakReference? * 不管內(nèi)存夠不夠蛹找,都會(huì)進(jìn)行回收? */publicstaticvoidmain(String[]args){WeakReference<Object>w=newWeakReference<Object>(newObject());System.out.println(w.get());System.gc();System.out.println(w.get());}
控制臺(tái)打印結(jié)果
java.lang.Object@2a139a55
null
可以看出姨伤,弱引用關(guān)聯(lián)的對(duì)象只能存活到下一次啟動(dòng)GC之前。
弱引用可以用來解決內(nèi)存泄露的問題庸疾,比如:ThreadLocal中的key就使用到了弱引用來防止內(nèi)存泄露乍楚,ThreadLocal的相關(guān)文章在末尾。
關(guān)系圖如下:
4. 虛引用(PhantomReference)
虛引用届慈,又稱作幻象引用徒溪,如果一個(gè)對(duì)象具有虛引用,那么它和沒有任何引用一樣金顿,被虛引用關(guān)聯(lián)的對(duì)象引用通過get方法獲取到的永遠(yuǎn)為null词渤,也就是說這種對(duì)象在任何時(shí)候都有可能被垃圾回收器回收,通過這種方式關(guān)聯(lián)的對(duì)象也無法調(diào)用對(duì)象中的方法串绩。虛引用主要是用來管理堆外內(nèi)存的缺虐,通過ReferenceQueue這個(gè)類實(shí)現(xiàn),當(dāng)一個(gè)對(duì)象被回收的時(shí)候礁凡,會(huì)向這個(gè)引用隊(duì)列里面添加相關(guān)數(shù)據(jù)高氮,給一個(gè)通知。
案例一:
Objectobj=newObject();PhantomReference<Object>objRef=newPhantomReference<Object>(obj,null);System.out.println("獲取虛引用所指向的對(duì)象"+objRef.get());System.out.println(objRef.get().equals(obj));//嘗試調(diào)用對(duì)象中的方法
控制臺(tái)打印結(jié)果
虛引用配合ReferenceQueue類顷牌,可以用來管理堆外內(nèi)存剪芍,如果虛引用對(duì)象被回收后,會(huì)向引用隊(duì)列里面發(fā)送一個(gè)通知窟蓝,可以參考以下demo便于理解罪裹。
案例二:
/** * 虛引用 *? 管理堆外內(nèi)存 */publicclassTest_PhantomReference{//引用隊(duì)列privatestaticfinalReferenceQueue<Object>QUEUE=newReferenceQueue<>();publicstaticvoidmain(String[]args){//當(dāng)虛引用對(duì)象被回收時(shí),會(huì)把一個(gè)信息填入到引用隊(duì)列中PhantomReference<Object>p=newPhantomReference<Object>(newObject(),QUEUE);System.out.println("第一次獲取虛引用指示的對(duì)象"+p.get());//nullSystem.out.println("第一次獲取虛引用的地址值"+p);List<byte[]>list=newArrayList<>();newThread(()->{booleanflag=true;try{while(flag){//不斷去new新的對(duì)象,內(nèi)存不足時(shí)GC就會(huì)啟動(dòng)list.add(newbyte[1024*1024]);}}catch(Exceptione){e.printStackTrace();}finally{flag=false;System.out.println("第二次獲取虛引用指示的對(duì)象"+p.get());}}).start();/* 再開啟一個(gè)線程,做一個(gè)監(jiān)控? * 當(dāng)虛引用被回收時(shí),會(huì)發(fā)送一個(gè)通知? * 如果引用隊(duì)列QUEUE中不再是null? * 證明虛引用已經(jīng)被回收? */newThread(()->{booleanflag=true;while(flag){Reference<?extendsObject>poll=QUEUE.poll();if(poll!=null){flag=false;System.out.println("虛引用對(duì)象"+poll+"被回收了");}}}).start();}}
虛引用可以用來管理堆外內(nèi)存状共,以上案例中我們結(jié)合了一個(gè)Queue來進(jìn)行測(cè)試套耕,開啟一個(gè)線程來進(jìn)行監(jiān)控,假如虛引用對(duì)象被回收那么通過poll方法就可以得知峡继。