1 Java的引用
對(duì)于Java中的垃圾回收機(jī)制來(lái)說(shuō)澳窑,對(duì)象是否被應(yīng)該回收的取決于該對(duì)象是否被引用钠怯。因此恋追,引用也是JVM進(jìn)行內(nèi)存管理的一個(gè)重要概念凭迹。Java中是JVM負(fù)責(zé)內(nèi)存的分配和回收,這是它的優(yōu)點(diǎn)(使用方便苦囱,程序不用再像使用C語(yǔ)言那樣擔(dān)心內(nèi)存)嗅绸,但同時(shí)也是它的缺點(diǎn)(不夠靈活)。由此撕彤,Java提供了引用分級(jí)模型鱼鸠,可以?定義Java對(duì)象重要性和優(yōu)先級(jí),提高JVM內(nèi)存回收的執(zhí)行效率?喉刘。
關(guān)于引用的定義瞧柔,在JDK1.2之前,如果reference類(lèi)型的數(shù)據(jù)中存儲(chǔ)的數(shù)值代表的是另一塊內(nèi)存的起始地址睦裳,就稱為這塊內(nèi)存代表著一個(gè)引用造锅;JDK1.2之后,Java對(duì)引用的概念進(jìn)行了擴(kuò)充廉邑,將引用分為強(qiáng)引用(Strong Reference)哥蔚、軟引用(Soft Reference)、弱引用(Weak Reference)蛛蒙、虛引用(Phantom Reference)四種糙箍。
軟引用對(duì)象和弱應(yīng)用對(duì)象主要用于:當(dāng)內(nèi)存空間還足夠,則能保存在內(nèi)存之中牵祟;如果內(nèi)存空間在垃圾收集之后還是非常緊張深夯,則可以拋棄這些對(duì)象。很多系統(tǒng)的緩存功能都符合這樣的使用場(chǎng)景。
而虛引用對(duì)象用于替代不靠譜的finalize方法咕晋,可以獲取對(duì)象的回收事件雹拄,來(lái)做資源清理工作。
2 對(duì)象生命周期
2.1 無(wú)分級(jí)引用對(duì)象生命周期
前面提到掌呜,分層引用的模型是用于內(nèi)存回收滓玖,沒(méi)有分級(jí)引用對(duì)象下,一個(gè)對(duì)象從創(chuàng)建到回收的生命周期可以簡(jiǎn)單地用下圖概括:對(duì)象被創(chuàng)建质蕉,被使用势篡,有資格被收集,最終被收集模暗,陰影區(qū)域表示對(duì)象“強(qiáng)可達(dá)”時(shí)間:
對(duì)象生命周期(無(wú)分級(jí)引用)
2.2 有分級(jí)引用對(duì)象生命周期
JDK1.2引入java.lang.ref程序包之后禁悠,對(duì)象的生命周期多了3個(gè)階段,軟可達(dá)汰蓉,弱可達(dá)绷蹲,虛可達(dá),這些狀態(tài)僅適用于符合垃圾回收條件的對(duì)象顾孽,這些對(duì)象處于非強(qiáng)引用階段,而且需要基于java.lang.ref包中的相關(guān)的引用對(duì)象類(lèi)來(lái)指示標(biāo)明比规。
軟可達(dá)
軟可達(dá)對(duì)象用SoftReference來(lái)指示標(biāo)明若厚,并沒(méi)有強(qiáng)引用,垃圾回收器會(huì)盡可能長(zhǎng)時(shí)間地保留對(duì)象蜒什,但是會(huì)在拋出OutOfMemoryError異常之前收集它测秸。
弱可達(dá)
弱可達(dá)對(duì)象用WeakReference來(lái)指示標(biāo)明,并沒(méi)有強(qiáng)引用或軟引用灾常,垃圾回收器會(huì)隨時(shí)回收對(duì)象霎冯,并不會(huì)嘗試保留它,但是會(huì)在拋出OutOfMemoryError異常之前收集它钞瀑。
在對(duì)象回收階段中沈撞,該對(duì)象在major collection期間被回收,但是可以在minor collection期間存活
虛可達(dá)
虛可達(dá)對(duì)象用PhantomReference來(lái)指示標(biāo)明雕什,它已經(jīng)被標(biāo)記選中進(jìn)行垃圾回收并且它的finalizer(如果有)已經(jīng)運(yùn)行缠俺。在這種情況下,術(shù)語(yǔ)“可達(dá)”實(shí)際上是用詞不當(dāng)贷岸,因?yàn)槟鸁o(wú)法訪問(wèn)實(shí)際對(duì)象壹士。
對(duì)象生命周期(有分級(jí)引用)
對(duì)象生命周期圖中添加三個(gè)新的可選狀態(tài)會(huì)造成一些困惑。邏輯順序上是從強(qiáng)可達(dá)到軟偿警,弱和虛躏救,最終到回收,但實(shí)際的情況取決于程序創(chuàng)建的參考對(duì)象螟蒸。但如果創(chuàng)建WeakReference但不創(chuàng)建SoftReference盒使,則對(duì)象直接從強(qiáng)可達(dá)到弱到達(dá)最終到收集崩掘。
3 強(qiáng)引用
強(qiáng)引用就是指在程序代碼之中普遍存在的,比如下面這段代碼中的obj和str都是強(qiáng)引用:
Object obj = new Object();
String str = "hello world";
只要強(qiáng)引用還存在忠怖,垃圾收集器永遠(yuǎn)不會(huì)回收被引用的對(duì)象呢堰,即使在內(nèi)存不足的情況下,JVM即使拋出OutOfMemoryError異常也不會(huì)回收這種對(duì)象凡泣。
實(shí)際使用上枉疼,可以通過(guò)把引用顯示賦值為null來(lái)中斷對(duì)象與強(qiáng)引用之前的關(guān)聯(lián),如果沒(méi)有任何引用執(zhí)行對(duì)象鞋拟,垃圾收集器將在合適的時(shí)間回收對(duì)象骂维。
例如ArrayList類(lèi)的remove方法中就是通過(guò)將引用賦值為null來(lái)實(shí)現(xiàn)清理工作的:
/**
? ? * Removes the element at the specified position in this list.
? ? * Shifts any subsequent elements to the left (subtracts one from their
? ? * indices).
? ? *
? ? * @param index the index of the element to be removed
? ? * @return the element that was removed from the list
? ? * @throws IndexOutOfBoundsException {@inheritDoc}
? ? */
? ? public E remove(int index) {
? ? ? ? rangeCheck(index);
? ? ? ? modCount++;
? ? ? ? E oldValue = elementData(index);
? ? ? ? int numMoved = size - index - 1;
? ? ? ? if (numMoved > 0)
? ? ? ? ? ? System.arraycopy(elementData, index+1, elementData, index,
? ? ? ? ? ? ? ? ? ? ? ? ? ? numMoved);
? ? ? ? elementData[--size] = null; // clear to let GC do its work
? ? ? ? return oldValue;
? ? }
4 引用對(duì)象
介紹軟引用、弱引用和虛引用之前贺纲,有必要介紹一下引用對(duì)象航闺,
引用對(duì)象是程序代碼和其他對(duì)象之間的間接層,稱為引用對(duì)象猴誊。每個(gè)引用對(duì)象都圍繞對(duì)象的引用構(gòu)造潦刃,并且不能更改引用值。
引用對(duì)象提供get()來(lái)獲得其引用值的一個(gè)強(qiáng)引用懈叹,垃圾收集器可能隨時(shí)回收引用值所指的對(duì)象乖杠。
一旦對(duì)象被回收,get()方法將返回null澄成,要正確使用引用對(duì)象胧洒,下面使用SoftReference(軟引用對(duì)象)作為參考示例:
/**
? ? * 簡(jiǎn)單使用demo
? ? */
? ? private static void simpleUseDemo(){
? ? ? ? List<String> myList = new ArrayList<>();
? ? ? ? SoftReference<List<String>> refObj = new SoftReference<>(myList);
? ? ? ? List<String> list = refObj.get();
? ? ? ? if (null != list) {
? ? ? ? ? ? list.add("hello");
? ? ? ? } else {
? ? ? ? ? ? // 整個(gè)列表已經(jīng)被垃圾回收了,做其他處理
? ? ? ? }
? ? }
也就是說(shuō)墨状,使用時(shí):
1卫漫、必須經(jīng)常檢查引用值是否為null?
垃圾收集器可能隨時(shí)回收引用對(duì)象,如果輕率地使用引用值肾砂,遲早會(huì)得到一個(gè)NullPointerException列赎。
2、必須使用強(qiáng)引用來(lái)指向引用對(duì)象返回的值?
垃圾收集器可能在任何時(shí)間回收引用對(duì)象通今,即使在一個(gè)表達(dá)式中間粥谬。
/**
? ? * 正確使用引用對(duì)象demo
? ? */
? ? private static void trueUseRefObjDemo(){
? ? ? ? List<String> myList = new ArrayList<>();
? ? ? ? SoftReference<List<String>> refObj = new SoftReference<>(myList);
? ? ? ? // 正確的使用,使用強(qiáng)引用指向?qū)ο蟊WC獲得對(duì)象之后不會(huì)被回收
? ? ? ? List<String> list = refObj.get();
? ? ? ? if (null != list) {
? ? ? ? ? ? list.add("hello");
? ? ? ? } else {
? ? ? ? ? ? // 整個(gè)列表已經(jīng)被垃圾回收了辫塌,做其他處理
? ? ? ? }
? ? }
? ? /**
? ? * 錯(cuò)誤使用引用對(duì)象demo
? ? */
? ? private static void falseUseRefObjDemo(){
? ? ? ? List<String> myList = new ArrayList<>();
? ? ? ? SoftReference<List<String>> refObj = new SoftReference<>(myList);
? ? ? ? // XXX 錯(cuò)誤的使用漏策,在檢查對(duì)象非空到使用對(duì)象期間,對(duì)象可能已經(jīng)被回收
? ? ? ? // 可能出現(xiàn)空指針異常
? ? ? ? if (null != refObj.get()) {
? ? ? ? ? ? refObj.get().add("hello");
? ? ? ? }
? ? }
3臼氨、必須持有引用對(duì)象的強(qiáng)引用?
如果創(chuàng)建引用對(duì)象掺喻,沒(méi)有持有對(duì)象的強(qiáng)引用,那么引用對(duì)象本身將被垃圾收集器回收。
4感耙、當(dāng)引用值沒(méi)有被其他強(qiáng)引用指向時(shí)褂乍,軟引用、弱引用和虛引用才會(huì)發(fā)揮作用即硼,引用對(duì)象的存在就是為了方便追蹤并高效垃圾回收逃片。
5 軟引用、弱引用和虛引用
引用對(duì)象的3個(gè)重要實(shí)現(xiàn)類(lèi)位于java.lang.ref包下只酥,分別是軟引用SoftReference褥实、弱引用WeakReference和虛引用PhantomReference。
5.1 軟引用
軟引用用來(lái)描述一些還有用但非必需的對(duì)象裂允。對(duì)于軟引用關(guān)聯(lián)著的對(duì)象损离,在系統(tǒng)將要發(fā)生拋出OutOfMemoryError異常之前,將會(huì)把這些對(duì)象列入回收范圍之內(nèi)進(jìn)行第二次回收绝编。如果這次回收還沒(méi)有足夠的內(nèi)存僻澎,才會(huì)拋出OutOfMemoryError異常。在JDK1.2之后十饥,提供了SoftReference類(lèi)來(lái)實(shí)現(xiàn)軟引用窟勃。
下面是一個(gè)使用示例:
import java.lang.ref.SoftReference;
public class SoftRefDemo {
? ? public static void main(String[] args) {
? ? ? ? SoftReference<String> sr = new SoftReference<>( new String("hello world "));
? ? ? ? // hello world
? ? ? ? System.out.println(sr.get());
? ? }
}
JDK文檔中提到:軟引用適用于對(duì)內(nèi)存敏感的緩存:每個(gè)緩存對(duì)象都是通過(guò)訪問(wèn)的 SoftReference,如果JVM決定需要內(nèi)存空間逗堵,那么它將清除回收部分或全部軟引用對(duì)應(yīng)的對(duì)象拳恋。如果它不需要空間,則SoftReference指示對(duì)象保留在堆中砸捏,并且可以通過(guò)程序代碼訪問(wèn)。在這種情況下隙赁,當(dāng)它們被積極使用時(shí)垦藏,它們被強(qiáng)引用,否則會(huì)被軟引用伞访。如果清除了軟引用掂骏,則需要刷新緩存。
實(shí)際使用上厚掷,要除非緩存的對(duì)象非常大弟灼,每個(gè)數(shù)量級(jí)為幾千字節(jié),才值得考慮使用軟引用對(duì)象冒黑。例如:實(shí)現(xiàn)一個(gè)文件服務(wù)器田绑,它需要定期檢索相同的文件,或者需要緩存大型對(duì)象圖抡爹。如果對(duì)象很小掩驱,必須清除很多對(duì)象才能產(chǎn)生影響,那么不建議使用,因?yàn)榍宄浺脤?duì)象會(huì)增加整個(gè)過(guò)程的開(kāi)銷(xiāo)欧穴。
5.2 弱引用
弱引用也是用來(lái)描述非必需對(duì)象民逼,但是它的強(qiáng)度比軟引用更弱一些,被弱引用關(guān)聯(lián)的對(duì)象只能生存到下一次垃圾收集發(fā)送之前涮帘。?當(dāng)垃圾收集器工作時(shí)拼苍,無(wú)論當(dāng)前內(nèi)存是否足夠,都會(huì)回收掉只被弱引用關(guān)聯(lián)的對(duì)象?调缨。
在JDK1.2之后疮鲫,提供了WeakReference類(lèi)來(lái)實(shí)現(xiàn)弱引用。
/**
? ? * 簡(jiǎn)單使用弱引用demo
? ? */
? ? private static void simpleUseWeakRefDemo(){
? ? ? ? WeakReference<String> sr = new WeakReference<>(new String("hello world " ));
? ? ? ? // before gc -> hello world
? ? ? ? System.out.println("before gc -> " + sr.get());
? ? ? ? // 通知JVM的gc進(jìn)行垃圾回收
? ? ? ? System.gc();
? ? ? ? // after gc -> null
? ? ? ? System.out.println("after gc -> " + sr.get());
? ? }
可以看到被弱引用關(guān)聯(lián)的對(duì)象同蜻,在gc之后被回收掉棚点。
有意思的地方是,如果把上面代碼中的:
WeakReference<String> sr = new WeakReference<>(new String("hello world "));
改為
WeakReference<String> sr = new WeakReference<>("hello world ");
程序?qū)⑤敵?/p>
before gc -> hello world
after gc -> hello world
這是因?yàn)槭褂肑ava的String直接賦值和使用new區(qū)別在于:
new 會(huì)在堆區(qū)創(chuàng)建一個(gè)可以被正惩迓回收的對(duì)象瘫析。
String直接賦值,會(huì)在Java StringPool(字符串常量池)里創(chuàng)建一個(gè)String對(duì)象默责,存于pergmen(永生代區(qū))中贬循,通常不會(huì)被gc回收。
WeakHashMap
為了更方便使用弱引用桃序,Java還提供了WeakHashMap杖虾,功能類(lèi)似HashMap,內(nèi)部實(shí)現(xiàn)是用弱引用對(duì)key進(jìn)行包裝媒熊,當(dāng)某個(gè)key對(duì)象沒(méi)有任何強(qiáng)引用指向奇适,gc會(huì)自動(dòng)回收key和value對(duì)象。
/**
? ? *? weakHashMap使用demo
? ? */
? ? private static void weakHashMapDemo(){
? ? ? ? WeakHashMap<String,String> weakHashMap = new WeakHashMap<>();
? ? ? ? String key1 = new String("key1");
? ? ? ? String key2 = new String("key2");
? ? ? ? String key3 = new String("key3");
? ? ? ? weakHashMap.put(key1, "value1");
? ? ? ? weakHashMap.put(key2, "value2");
? ? ? ? weakHashMap.put(key3, "value3");
? ? ? ? // 使沒(méi)有任何強(qiáng)引用指向key1
? ? ? ? key1 = null;
? ? ? ? System.out.println("before gc weakHashMap = " + weakHashMap + " , size=" + weakHashMap.size());
? ? ? ? // 通知JVM的gc進(jìn)行垃圾回收
? ? ? ? System.gc();
? ? ? ? System.out.println("after gc weakHashMap = " + weakHashMap + " , size="+ weakHashMap.size());
? ? }
程序輸出:
before: gc weakHashMap = {key1=value1, key2=value2, key3=value3} , size=3
after: gc weakHashMap = {key2=value2, key3=value3} , size=2
WeakHashMap比較適用于緩存的場(chǎng)景芦鳍,例如Tomcat的緩存就用到嚷往。
5.3 引用隊(duì)列
介紹虛引用之前,先介紹引用隊(duì)列:
在使用引用對(duì)象時(shí)柠衅,通過(guò)判斷get()方法返回的值是否為null來(lái)判斷對(duì)象是否已經(jīng)被回收皮仁,當(dāng)這樣做并不是非常高效,特別是當(dāng)我們有很多引用對(duì)象菲宴,如果想找出哪些對(duì)象已經(jīng)被回收贷祈,需要遍歷所有所有對(duì)象。
更好的方案是使用引用隊(duì)列喝峦,在構(gòu)造引用對(duì)象時(shí)與隊(duì)列關(guān)聯(lián)势誊,當(dāng)gc(垃圾回收線程)準(zhǔn)備回收一個(gè)對(duì)象時(shí),如果發(fā)現(xiàn)它還僅有軟引用(或弱引用愈犹,或虛引用)指向它键科,就會(huì)在回收該對(duì)象之前闻丑,把這個(gè)軟引用(或弱引用,或虛引用)加入到與之關(guān)聯(lián)的引用隊(duì)列(ReferenceQueue)中勋颖。
如果一個(gè)軟引用(或弱引用嗦嗡,或虛引用)對(duì)象本身在引用隊(duì)列中,就說(shuō)明該引用對(duì)象所指向的對(duì)象被回收了饭玲,所以要找出所有被回收的對(duì)象侥祭,只需要遍歷引用隊(duì)列。
當(dāng)軟引用(或弱引用茄厘,或虛引用)對(duì)象所指向的對(duì)象被回收了矮冬,那么這個(gè)引用對(duì)象本身就沒(méi)有價(jià)值了,如果程序中存在大量的這類(lèi)對(duì)象(注意次哈,我們創(chuàng)建的軟引用胎署、弱引用、虛引用對(duì)象本身是個(gè)強(qiáng)引用窑滞,不會(huì)自動(dòng)被gc回收)琼牧,就會(huì)浪費(fèi)內(nèi)存。因此我們這就可以手動(dòng)回收位于引用隊(duì)列中的引用對(duì)象本身哀卫。
/**
? ? * 引用隊(duì)列demo
? ? */
? ? private static void refQueueDemo() {
? ? ? ? ReferenceQueue<String> refQueue = new ReferenceQueue<>();
? ? ? ? // 用于檢查引用隊(duì)列中的引用值被回收
? ? ? ? Thread checkRefQueueThread = new Thread(() -> {
? ? ? ? ? ? while (true) {
? ? ? ? ? ? ? ? Reference<? extends String> clearRef = refQueue.poll();
? ? ? ? ? ? ? ? if (null != clearRef) {
? ? ? ? ? ? ? ? ? ? System.out
? ? ? ? ? ? ? ? ? ? ? ? ? ? .println("引用對(duì)象被回收, ref = " + clearRef + ", value = " + clearRef.get());
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? });
? ? ? ? checkRefQueueThread.start();
? ? ? ? WeakReference<String> weakRef1 = new WeakReference<>(new String("value1"), refQueue);
? ? ? ? WeakReference<String> weakRef2 = new WeakReference<>(new String("value2"), refQueue);
? ? ? ? WeakReference<String> weakRef3 = new WeakReference<>(new String("value3"), refQueue);
? ? ? ? System.out.println("ref1 value = " + weakRef1.get() + ", ref2 value = " + weakRef2.get()
? ? ? ? ? ? ? ? + ", ref3 value = " + weakRef3.get());
? ? ? ? System.out.println("開(kāi)始通知JVM的gc進(jìn)行垃圾回收");
? ? ? ? // 通知JVM的gc進(jìn)行垃圾回收
? ? ? ? System.gc();
? ? }
程序輸出:
ref1 value = value1, ref2 value = value2, ref3 value = value3
開(kāi)始通知JVM的gc進(jìn)行垃圾回收
引用對(duì)象被回收, ref = java.lang.ref.WeakReference@48c6cd96, value=null
引用對(duì)象被回收, ref = java.lang.ref.WeakReference@46013afe, value=null
引用對(duì)象被回收, ref = java.lang.ref.WeakReference@423ea6e6, value=null
5.4 虛引用
虛引用也稱為幽靈引用或者幻影引用巨坊,不同于軟引用和弱引用,虛引用不用于訪問(wèn)引用對(duì)象所指示的對(duì)象此改,相反趾撵,?通過(guò)不斷輪詢虛引用對(duì)象關(guān)聯(lián)的引用隊(duì)列,可以得到對(duì)象回收事件?共啃。一個(gè)對(duì)象是否有虛引用的存在占调,完全不會(huì)對(duì)其生產(chǎn)時(shí)間構(gòu)成影響,也無(wú)法通過(guò)虛引用來(lái)取得一個(gè)對(duì)象實(shí)例移剪。雖然這看起來(lái)毫無(wú)意義妈候,但它實(shí)際上可以用來(lái)做對(duì)象回收時(shí)?資源清理、釋放?挂滓,它比f(wàn)inalize更靈活,我們可以基于虛引用做更安全可靠的對(duì)象關(guān)聯(lián)的資源回收啸胧。
finalize的問(wèn)題
Java語(yǔ)言規(guī)范并不保證finalize方法會(huì)被及時(shí)地執(zhí)行赶站、而且根本不會(huì)保證它們會(huì)被執(zhí)行?
如果可用內(nèi)存沒(méi)有被耗盡,垃圾收集器不會(huì)運(yùn)行纺念,finalize方法也不會(huì)被執(zhí)行贝椿。
性能問(wèn)題?
JVM通常在單獨(dú)的低優(yōu)先級(jí)線程中完成finalize的執(zhí)行。
對(duì)象再生問(wèn)題?
finalize方法中陷谱,可將待回收對(duì)象賦值給GC Roots可達(dá)的對(duì)象引用烙博,從而達(dá)到對(duì)象再生的目的瑟蜈。
針對(duì)不靠譜finalize方法,完全可以使用虛引用來(lái)實(shí)現(xiàn)渣窜。在JDK1.2之后铺根,提供了PhantomReference類(lèi)來(lái)實(shí)現(xiàn)虛引用。
下面是簡(jiǎn)單的使用例子乔宿,通過(guò)訪問(wèn)引用隊(duì)列可以得到對(duì)象的回收事件:
/**
? ? * 簡(jiǎn)單使用虛引用demo
? ? * 虛引用在實(shí)現(xiàn)一個(gè)對(duì)象被回收之前必須做清理操作是很有用的,比f(wàn)inalize()方法更靈活
? ? */
? ? private static void simpleUsePhantomRefDemo() throws InterruptedException {
? ? ? ? Object obj = new Object();
? ? ? ? ReferenceQueue<Object> refQueue = new ReferenceQueue<>();
? ? ? ? PhantomReference<Object> phantomRef = new PhantomReference<>(obj, refQueue);
? ? ? ? // null
? ? ? ? System.out.println(phantomRef.get());
? ? ? ? // null
? ? ? ? System.out.println(refQueue.poll());
? ? ? ? obj = null;
? ? ? ? // 通知JVM的gc進(jìn)行垃圾回收
? ? ? ? System.gc();
? ? ? ? // null, 調(diào)用phantomRef.get()不管在什么情況下會(huì)一直返回null
? ? ? ? System.out.println(phantomRef.get());
? ? ? ? // 當(dāng)GC發(fā)現(xiàn)了虛引用位迂,GC會(huì)將phantomRef插入進(jìn)我們之前創(chuàng)建時(shí)傳入的refQueue隊(duì)列
? ? ? ? // 注意,此時(shí)phantomRef對(duì)象详瑞,并沒(méi)有被GC回收掂林,在我們顯式地調(diào)用refQueue.poll返回phantomRef之后
? ? ? ? // 當(dāng)GC第二次發(fā)現(xiàn)虛引用,而此時(shí)JVM將phantomRef插入到refQueue會(huì)插入失敗坝橡,此時(shí)GC才會(huì)對(duì)phantomRef對(duì)象進(jìn)行回收
? ? ? ? Thread.sleep(200);
? ? ? ? Reference<?> pollObj = refQueue.poll();
? ? ? ? // java.lang.ref.PhantomReference@1540e19d
? ? ? ? System.out.println(pollObj);
? ? ? ? if (null != pollObj) {
? ? ? ? ? ? // 進(jìn)行資源回收的操作
? ? ? ? }
? ? }
比較常見(jiàn)的泻帮,可以基于虛引用實(shí)現(xiàn)JDBC連接池,鎖的釋放等場(chǎng)景计寇。
以連接池為例锣杂,調(diào)用方正常情況下使用完連接,需要把連接釋放回池中饲常,但是不可避免有可能程序有bug蹲堂,造成連接沒(méi)有正常釋放回池中”从伲基于虛引用對(duì)Connection對(duì)象進(jìn)行包裝柒竞,并關(guān)聯(lián)引用隊(duì)列,就可以通過(guò)輪詢引用隊(duì)列檢查哪些連接對(duì)象已經(jīng)被GC回收播聪,釋放相關(guān)連接資源朽基。?具體實(shí)現(xiàn)已上傳github的caison-blog-demo倉(cāng)庫(kù)?。
6 總結(jié)
對(duì)比一下幾種引用對(duì)象的不同:
引用類(lèi)型 GC回收時(shí)間 常見(jiàn)用途 生存時(shí)間
強(qiáng)引用 永不 對(duì)象的一般狀態(tài) JVM停止運(yùn)行時(shí)
軟引用 內(nèi)存不足時(shí) 對(duì)象緩存 內(nèi)存不足時(shí)終止
弱引用 GC時(shí) 對(duì)象緩存 GC后終止
虛引用离陶,配合引用隊(duì)列使用稼虎,通過(guò)不斷輪詢引用隊(duì)列獲取對(duì)象回收事件。
雖然引用對(duì)象是一個(gè)非常有用的工具來(lái)管理你的內(nèi)存消耗招刨,但有時(shí)它們是不夠的霎俩,或者是過(guò)度設(shè)計(jì)的 。例如沉眶,使用一個(gè)Map來(lái)緩存從數(shù)據(jù)庫(kù)中讀取的數(shù)據(jù)打却。雖然可以使用弱引用來(lái)作為緩存,但最終程序需要運(yùn)行一定量的內(nèi)存谎倔。如果不能給它足夠?qū)嶋H足夠的資源完成任何工作柳击,那么錯(cuò)誤恢復(fù)機(jī)制有多強(qiáng)大也沒(méi)有用。
當(dāng)遇到OutOfMemoryError錯(cuò)誤片习,第一反應(yīng)是要弄清楚它為什么會(huì)發(fā)生捌肴,也許真的是程序有bug蹬叭,也許是可用內(nèi)存設(shè)置的太低。
在開(kāi)發(fā)過(guò)程中状知,應(yīng)該制定程序具體的使用內(nèi)存大小秽五,而已要關(guān)注實(shí)際使用中用了多少內(nèi)存。大多數(shù)應(yīng)用程序在實(shí)際運(yùn)行負(fù)載下试幽,程序的內(nèi)存占用會(huì)達(dá)到穩(wěn)定狀態(tài)筝蚕,可以用此來(lái)作為參考來(lái)設(shè)置合理的堆大小。如果程序的內(nèi)存使用量隨著時(shí)間的推移而上升铺坞,很有可能是因?yàn)楫?dāng)對(duì)象不再使用時(shí)仍然擁有對(duì)對(duì)象的強(qiáng)引用起宽。引用對(duì)象在這里可能會(huì)有所幫助,但更有可能是把它當(dāng)做一個(gè)bug來(lái)進(jìn)行修復(fù)济榨。
如果想學(xué)習(xí)Java工程化坯沪、高性能及分布式、深入淺出擒滑。性能調(diào)優(yōu)腐晾、Spring,MyBatis丐一,Netty源碼分析的朋友可以加我的Java高級(jí)架構(gòu)進(jìn)階群:180705916藻糖,群里有阿里大牛直播講解技術(shù),以及Java大型互聯(lián)網(wǎng)技術(shù)的視頻免費(fèi)分享給大家
---------------------
作者:SJYUA
來(lái)源:CSDN
原文:https://blog.csdn.net/yexunce3159/article/details/83059975?utm_source=copy
版權(quán)聲明:本文為博主原創(chuàng)文章库车,轉(zhuǎn)載請(qǐng)附上博文鏈接巨柒!