介紹
Java中提供了四種引用類型埠褪,分別如下:
- StrongReference(強(qiáng)引用)
- SoftReference(軟引用)
- WeakReference(弱引用)
- PhantomReference(虛引用)
其中StrongReference是包權(quán)限無法使用浓利,其它三種引用類型都是公共的可以在應(yīng)用中使用,下面是Reference的類結(jié)構(gòu)钞速。
StrongReference
Java中的強(qiáng)引用其實(shí)就是new對(duì)象贷掖,可以通過引用操作堆中的對(duì)象(和C中的指針類似),例如:
StringBuffer buffer = new StringBuffer("HelloWorld!");
變量buffer指向StringBuffer所在的堆空間渴语,通過buffer來進(jìn)行操作苹威。
StrongReference的特性
1.可以直接訪問目標(biāo)對(duì)象;
2.指向的對(duì)象不會(huì)被GC回收??遵班,當(dāng)JVM內(nèi)存不足時(shí)會(huì)拋出OOM異常終端程序屠升;
3.基于上面第2點(diǎn),當(dāng)其它需要釋放的代碼塊持StrongReference會(huì)造成內(nèi)存泄露狭郑;
SoftReference
SoftReference即軟引用腹暖,當(dāng)JVM堆空間使用率到達(dá)閾值的時(shí)候會(huì)觸發(fā)GC回收,我們可以用它來實(shí)現(xiàn)對(duì)內(nèi)存敏感的緩存翰萨,SoftReference的特性是可以保留對(duì)Java對(duì)象軟引用的實(shí)例脏答,軟引用的實(shí)例并不會(huì)阻止GC進(jìn)行回收,在GC線程進(jìn)行回收之前我們可以通過它的get方法獲取到對(duì)象的強(qiáng)引用亩鬼,一旦對(duì)象被GC回收后get方法將返回null殖告。
下面我們通過代碼來實(shí)踐,通過設(shè)置JVM堆內(nèi)存大小為2M來模擬:
java -Xms2M -Xmx2M -verbose:gc -XX:+PrintGCDetails [class文件]
- -Xms2M
堆大小固定為2M - -verbose:gc
輸出虛擬機(jī)中GC的詳細(xì)情況 - -XX:+PrintGCDetails
在控制臺(tái)上打印出GC具體細(xì)節(jié)
public static void testSoftRef() {
SoftReference<byte[]> softRef1 = new SoftReference<>(new byte[1024 * 300]);
SoftReference<byte[]> softRef2 = new SoftReference<>(new byte[1024 * 300]);
SoftReference<byte[]> softRef3 = new SoftReference<>(new byte[1024 * 300]);
SoftReference<byte[]> softRef4 = new SoftReference<>(new byte[1024 * 300]);
SoftReference<byte[]> softRef5 = new SoftReference<>(new byte[1024 * 300]);
System.out.println(softRef1.get());
System.out.println(softRef2.get());
System.out.println(softRef3.get());
System.out.println(softRef4.get());
System.out.println(softRef5.get());
}
結(jié)果如下雳锋,我們可以看到上面分配的byte長度是超出年輕代大小的黄绩,當(dāng)內(nèi)存不足時(shí)的確觸發(fā)了GC進(jìn)行回收,正如上面所說的那樣玷过。
[GC (Allocation Failure) [PSYoungGen: 510K->320K(1024K)] 510K->320K(1536K), 0.0037082 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 825K->432K(1024K)] 825K->432K(1536K), 0.0016384 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 942K->496K(1024K)] 942K->512K(1536K), 0.0113468 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
[GC (Allocation Failure) --[PSYoungGen: 984K->984K(1024K)] 1300K->1493K(1536K), 0.0014734 secs] [Times: user=0.00 sys=0.01, real=0.00 secs]
[Full GC (Ergonomics) [PSYoungGen: 984K->34K(1024K)] [ParOldGen: 509K->493K(512K)] 1493K->527K(1536K), [Metaspace: 3282K->3282K(1056768K)], 0.0131428 secs] [Times: user=0.01 sys=0.00, real=0.02 secs]
[Full GC (Ergonomics) [PSYoungGen: 334K->334K(1024K)] [ParOldGen: 493K->492K(512K)] 827K->827K(1536K), [Metaspace: 3282K->3282K(1056768K)], 0.0106514 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[Full GC (Allocation Failure) [PSYoungGen: 334K->0K(1024K)] [ParOldGen: 492K->397K(512K)] 827K->397K(1536K), [Metaspace: 3282K->3282K(1056768K)], 0.0042212 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 300K->332K(1024K)] 697K->729K(1536K), 0.0002648 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
null
null
null
[B@610455d6
[B@511d50c0
Heap
PSYoungGen total 1024K, used 646K [0x00000007bfe80000, 0x00000007c0000000, 0x00000007c0000000)
eden space 512K, 61% used [0x00000007bfe80000,0x00000007bfecea50,0x00000007bff00000)
from space 512K, 64% used [0x00000007bff80000,0x00000007bffd3010,0x00000007c0000000)
to space 512K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007bff80000)
ParOldGen total 512K, used 397K [0x00000007bfe00000, 0x00000007bfe80000, 0x00000007bfe80000)
object space 512K, 77% used [0x00000007bfe00000,0x00000007bfe63570,0x00000007bfe80000)
Metaspace used 3291K, capacity 4500K, committed 4864K, reserved 1056768K
class space used 363K, capacity 388K, committed 512K, reserved 1048576K
引用隊(duì)列(ReferenceQueue)
在很多場景下爽丹,我們的程序需要在一個(gè)對(duì)象的可達(dá)性(是否已經(jīng)被GC回收)發(fā)生變化時(shí)得到通知筑煮,引用隊(duì)列就是用于收集這些信息的隊(duì)列。在創(chuàng)建SoftReference對(duì)象時(shí)粤蝎,可以為其關(guān)聯(lián)一個(gè)引用隊(duì)列真仲,當(dāng)SoftReference所引用的對(duì)象被GC回收時(shí),Java虛擬機(jī)就會(huì)將該SoftReference對(duì)象添加到與之關(guān)聯(lián)的引用隊(duì)列中初澎。當(dāng)需要檢測這些通知信息時(shí)秸应,就可以從引用隊(duì)列中獲取這些SoftReference對(duì)象。不僅是SoftReference碑宴,下面介紹的弱引用和虛引用都可以關(guān)聯(lián)相應(yīng)的隊(duì)列软啼。
WeakReference
WeakReference即弱引用,當(dāng)觸發(fā)GC時(shí)延柠,無論JVM堆內(nèi)存是否足夠?qū)ο蠖紩?huì)被回收焰宣,下面進(jìn)行測試:
public static void testWeakRef() {
byte[] buffer = new byte[1024 * 500];
WeakReference<byte[]> weakReference = new WeakReference<>(buffer);
System.out.println("GC前:" + weakReference.get());
buffer = null;
//手動(dòng)觸發(fā)GC
System.gc();
System.out.println("GC后:" + weakReference.get());
}
結(jié)果如下:
GC前:[B@610455d6
GC后:null
SoftReference和WeakReference都適用于保存可選的緩存數(shù)據(jù),在系統(tǒng)內(nèi)存不足時(shí)捕仔,將回收緩存的數(shù)據(jù)不會(huì)導(dǎo)致OOM,并且緩存也能存在很長一段時(shí)間盈罐。
PhantomReference
PhantomReference即虛引用榜跌,是所有類型中最弱的,它幾乎是沒有引用因?yàn)殡S時(shí)會(huì)被GC回收盅粪,當(dāng)調(diào)用它的get方法獲取強(qiáng)引用時(shí)始終都是返回null钓葫,它必須要和ReferenceQueue一起使用,用來跟蹤垃圾回收過程票顾。
當(dāng)GC要回收對(duì)象時(shí)础浮,如果發(fā)現(xiàn)PhantomReference后將進(jìn)行GC然后銷毀該對(duì)象,并將PhantomReference添加到ReferenceQueue中奠骄,判斷是否向ReferenceQueue添加了PhantomReference可以確定是否需要對(duì)引用的對(duì)象進(jìn)行回收豆同,如果ReferenceQueue中存在PhantomReference那么在回收引用對(duì)象之前可以進(jìn)行一些額外的操作。
public static void testPhantomRef() {
byte[] buffer = new byte[1024 * 500];
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
PhantomReference<byte[]> phantomReference = new PhantomReference<>(buffer,referenceQueue);
buffer = null;
System.out.println("GC前:" + phantomReference.get());
System.gc();
System.out.println("GC后:" + phantomReference.get());
}
結(jié)果如下:
GC前:null
GC后:null
關(guān)于PhantomReference的get方法總是返回null含鳞。
/**
* Returns this reference object's referent. Because the referent of a
* phantom reference is always inaccessible, this method always returns
* <code>null</code>.
*
* @return <code>null</code>
*/
public T get() {
return null;
}
WeakHashMap
顧名思義影锈,它和HashMap一樣都是實(shí)現(xiàn)了Map接口,只不過它使用的是WeakReference作為存儲(chǔ)蝉绷,WeakHashMap是典型的弱引用例子鸭廷。
Entry弱引用key,強(qiáng)引用value熔吗;當(dāng)不再由強(qiáng)引用指向key時(shí)辆床,則key可以被垃圾回收,當(dāng)key被垃圾回收之后桅狠,對(duì)應(yīng)的Entry對(duì)象會(huì)被Java虛擬機(jī)加入到其關(guān)聯(lián)的隊(duì)列中讼载。當(dāng)應(yīng)用程序下次操作WeakHashMap時(shí)轿秧,例如對(duì)WeakHashMap的擴(kuò)容操作,就會(huì)遍歷關(guān)聯(lián)的引用隊(duì)列维雇,將其中的Entry對(duì)象從WeakHashMap中刪除淤刃。
public class WeakHashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V> {}
private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {}
需要注意的是如果WeakHashMap的key在系統(tǒng)中是StrongReference強(qiáng)引用的, 那么WeakHashMap將退化為一個(gè)普通的HashMap,因?yàn)樗荒鼙籊C回收吱型。