java內(nèi)存管理分為內(nèi)存分配和內(nèi)存回收,都不需要程序員負(fù)責(zé),垃圾回收的機(jī)制主要是看對象是否有引用指向該對象
- java對象的引用包括
強(qiáng)引用货邓,軟引用笙隙,弱引用,虛引用 - Java中提供這四種引用類型主要有兩個目的:
1. 是可以讓程序員通過代碼的方式?jīng)Q定某些對象的生命周期;
2. 是有利于JVM進(jìn)行垃圾回收。
一、 強(qiáng)引用(StrongReference)
- 強(qiáng)引用是使用最普遍的引用巴柿。只要某個對象有強(qiáng)引用與之關(guān)聯(lián),JVM必定不會回收這個對象死遭,即使在內(nèi)存不足的情況下广恢,JVM寧愿拋出OutOfMemory錯誤也不會回收這種對象
如下:
Object object =new Object();
String str ="hello";
如果想中斷強(qiáng)引用和某個對象之間的關(guān)聯(lián),可以顯示地將引用賦值為null殃姓,這樣一來的話袁波,JVM在合適的時間就會回收該對象瓦阐,如下:
object = null;
str = null;
再看一下源碼中如何解決強(qiáng)引用轉(zhuǎn)為軟引用后的回收
/**
* 這是ArrayList中的清空數(shù)組的方法
*/
private transient Object[] elementData;
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
注:在ArrayList類中定義了一個私有的變量elementData數(shù)組,在調(diào)用方法清空數(shù)組時可以看到為每個數(shù)組內(nèi)容賦值為null篷牌。不同于elementData=null睡蟋,強(qiáng)引用仍然存在,避免在后續(xù)調(diào)用 add()等方法添加元素時進(jìn)行重新的內(nèi)存分配枷颊。使用如clear()方法中釋放內(nèi)存的方法對數(shù)組中存放的引用類型特別適用戳杀,這樣就可以及時釋放內(nèi)存。
二夭苗、 軟引用(SoftReference)
- 軟引用是用來描述一些有用但并不是必需的對象信卡,在Java中用java.lang.ref.SoftReference類來表示。只有在內(nèi)存不足的時候JVM才會回收該對象题造。
如下:
String str=new String("abc"); // 強(qiáng)引用
SoftReference<String> softRef=new SoftReference<String>(str); // 軟引用
當(dāng)內(nèi)存不足時傍菇,等價于:
If(JVM.內(nèi)存不足()) {
str = null; // 轉(zhuǎn)換為軟引用
System.gc(); // 垃圾回收器進(jìn)行回收
}
- 因此,這一點(diǎn)可以很好地用來解決OOM的問題界赔,并且這個特性很適合用來實(shí)現(xiàn)緩存:比如網(wǎng)頁緩存丢习、圖片緩存等。
在瀏覽頁面時淮悼,我們會經(jīng)常使用回退功能咐低,通常有兩種程序?qū)崿F(xiàn)方式:
(1)將用戶的過去查看過的用戶信息保存在內(nèi)存中,每個存儲用戶信息的Java對象的生命周期貫穿整個應(yīng)用程序的始終袜腥;缺陷:這樣會造成內(nèi)存的大量浪費(fèi)见擦,甚至?xí)斐蓛?nèi)存溢出。
(2)如果一個頁面瀏覽結(jié)束羹令,就對其進(jìn)行內(nèi)容的回收鲤屡,只存儲當(dāng)前頁面的用戶信息,當(dāng)用戶進(jìn)行回退操作時福侈,重新構(gòu)建該用戶信息执俩;缺陷:即使垃圾收集線程還沒有進(jìn)行垃圾收集,存儲用戶信息的對象仍然完好地保存在內(nèi)存中癌刽,應(yīng)用程序也要重新構(gòu)建一個對象。
注:我們大都知道尝丐,訪問磁盤文件显拜、訪問網(wǎng)絡(luò)資源、查詢數(shù)據(jù)庫等操作都是影響應(yīng)用程序執(zhí)行性能的重要因素爹袁,如果能重新獲取那些尚未被回收的Java對象的引用远荠,必將減少不必要的訪問,大大提高程序的運(yùn)行速度失息。
- 因此看一下使用軟引用如何解決該問譬淳,如下瀏覽器網(wǎng)頁緩存實(shí)例:
Browser prev = new Browser(); // 獲取頁面進(jìn)行瀏覽
SoftReference sr = new SoftReference(prev); // 瀏覽完畢后置為軟引用
if (sr.get() != null) {
rev = (Browser)sr.get(); // 還沒有被回收器回收档址,直接獲取
} else {
prev = new Browser(); // 由于內(nèi)存吃緊,軟引用對象被回收
sr = new SoftReference(prev); // 重新構(gòu)建
}
- 軟引用可以和一個引用隊列(ReferenceQueue)聯(lián)合使用邻梆,如果軟引用所引用的對象被垃圾回收器回收守伸,Java虛擬機(jī)就會把這個軟引用加入到與之關(guān)聯(lián)的引用隊列中。
三浦妄、弱引用(WeakReference)
- 只具有弱引用的對象擁有更短暫的生命周期尼摹。在垃圾回收器線程掃描它所管轄的內(nèi)存區(qū)域的過程中,一旦發(fā)現(xiàn)了只具有弱引用的對象剂娄,不管當(dāng)前內(nèi)存空間足夠與否蠢涝,都會回收它的內(nèi)存。不過阅懦,由于垃圾回收器是一個優(yōu)先級很低的線程和二,因此不一定會很快發(fā)現(xiàn)那些只具有弱引用的對象。
// 創(chuàng)建一個引用隊列
ReferenceQueue<String> rq = new ReferenceQueue<String>();
// 實(shí)現(xiàn)一個弱引用耳胎,將強(qiáng)引用類型hello和是實(shí)例化的rq放到弱引用實(shí)現(xiàn)里面
WeakReference<String> sr = new WeakReference<String>(new String("hello"), rq);
// 通過弱引用get方法獲取強(qiáng)引用中創(chuàng)建的內(nèi)存空間hello值
System.out.println(sr.get());
System.gc(); //通知JVM的gc進(jìn)行垃圾回收
System.out.println(sr.get());
四惯吕、 虛引用(PhantomReference)
- 虛引用也稱為幻影引用,一個對象是都有虛引用的存在都不會對生存時間都構(gòu)成影響场晶,也無法通過虛引用來獲取對一個對象的真實(shí)引用混埠。唯一的用處:能在對象被GC時收到系統(tǒng)通知,JAVA中用PhantomReference來實(shí)現(xiàn)虛引用
- 虛引用必須和引用隊列 (ReferenceQueue)聯(lián)合使用诗轻。當(dāng)垃圾回收器準(zhǔn)備回收一個對象時钳宪,如果發(fā)現(xiàn)它還有虛引用,就會在回收對象的內(nèi)存之前扳炬,把這個虛引用加入到與之 關(guān)聯(lián)的引用隊列中吏颖。
程序可以通過判斷引用隊列中是否已經(jīng)加入了虛引用,來了解被引用的對象是否將要被垃圾回收恨樟。如果程序發(fā)現(xiàn)某個虛引用已經(jīng)被加入到引用隊列半醉,那么就可以在所引用的對象的內(nèi)存被回收之前采取必要的行動。