1. 概述
在Java虛擬機(jī)垃圾收集這一文中,我們談到了Java中對(duì)引用的定義有如下四種:強(qiáng)引用伏伐,軟引用,弱引用晕拆,虛引用四種藐翎。接下來我們就具體談一談這四種引用。
2. 強(qiáng)引用
Java語言通過引用使得我們可以直接操作堆中的對(duì)象实幕,下例中的變量str指向String實(shí)例所在的堆空間吝镣,通過str我們可以操作該對(duì)象
String str = new String("StrongReference");
強(qiáng)引用具有如下的特點(diǎn):
- 可以直接訪問目標(biāo)對(duì)象
- 所指向的對(duì)象在任何時(shí)候都不會(huì)被系統(tǒng)回收,JVM寧愿拋出OOM異常昆庇,也不會(huì)回收強(qiáng)引用所指向的對(duì)象
- 可能會(huì)導(dǎo)致內(nèi)存泄漏
3. 軟引用
軟引用是除了強(qiáng)引用外最強(qiáng)的引用類型末贾,我們可以通過java.lang.ref.SoftReference來使用軟引用。SoftReference實(shí)例可以保存對(duì)一個(gè)Java對(duì)象的軟引用整吆,在不妨礙垃圾收集器對(duì)該Java對(duì)象進(jìn)行回收的前提下拱撵,也就是在該對(duì)象被垃圾回收器回收之前,通過SoftReference類的get()方法可以獲得該Java對(duì)象的強(qiáng)引用掂为,一旦該對(duì)象被回收之后裕膀,get()就會(huì)返回null。
/**jvm參數(shù) :-XX:+PrintGCDetails -Xmx4m -Xms4m -Xmn4m */
public class ReferenceTest {
public static void main(String[] args) {
byte[] byte1 = new byte[1024 * 515];
SoftReference softReference = new SoftReference(byte1);
byte1 = null;
System.gc();
System.out.println("obj 是否被回收 勇哗? "+softReference.get());
}
}
/********************************************************************************************/
/**
obj 是否被回收 昼扛? [B@7cd84586
Heap
PSYoungGen total 3072K, used 1108K [0x00000000ffc80000, 0x0000000100000000, 0x0000000100000000)
eden space 2560K, 43% used [0x00000000ffc80000,0x00000000ffd952a8,0x00000000fff00000)
from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
to space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
ParOldGen total 512K, used 454K [0x00000000ffc00000, 0x00000000ffc80000, 0x00000000ffc80000)
object space 512K, 88% used [0x00000000ffc00000,0x00000000ffc71898,0x00000000ffc80000)
Metaspace used 3231K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 352K, capacity 388K, committed 512K, reserved 1048576K
Java HotSpot(TM) 64-Bit Server VM warning: MaxNewSize (4096k) is equal to or greater than the entire heap (4096k). A new max generation size of 3584k will be used.
*/
這是雖然進(jìn)行了一次GC但是通過軟引用還是能取得對(duì)象,接下來我們再申請(qǐng)一塊大一點(diǎn)的空間
public class ReferenceTest {
public static void main(String[] args) {
byte[] byte1 = new byte[1024 * 515];
SoftReference softReference = new SoftReference(byte1);
byte1 = null;
System.gc();
byte[] byte2 = new byte[1024 * 2500];
System.out.println("obj 是否被回收 欲诺? "+softReference.get());
}
}
/**********************************************************************************************/
/**
obj 是否被回收 抄谐? null
[Full GC (Ergonomics) [PSYoungGen: 2560K->0K(3072K)] [ParOldGen: 467K->464K(512K)] 3027K->464K(3584K), [Metaspace: 3313K->3313K(1056768K)], 0.0056548 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
Heap
PSYoungGen total 3072K, used 25K [0x00000000ffc80000, 0x0000000100000000, 0x0000000100000000)
eden space 2560K, 1% used [0x00000000ffc80000,0x00000000ffc867f0,0x00000000fff00000)
from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
to space 512K, 85% used [0x00000000fff00000,0x00000000fff6ce98,0x00000000fff80000)
ParOldGen total 512K, used 464K [0x00000000ffc00000, 0x00000000ffc80000, 0x00000000ffc80000)
object space 512K, 90% used [0x00000000ffc00000,0x00000000ffc74198,0x00000000ffc80000)
Metaspace used 3319K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 359K, capacity 388K, committed 512K, reserved 1048576K
*/
此時(shí),PSYoungGen(年輕代)中的2560K空間肯定是不夠用的扰法,就會(huì)觸發(fā)一次垃圾回收蛹含,當(dāng)然你也可以顯示的再調(diào)用一次gc(),經(jīng)過這次GC后軟引用已經(jīng)被回收了塞颁。因此我們可以總結(jié)出軟引用有如下特點(diǎn):
- 一個(gè)持軟引用的對(duì)象浦箱,不會(huì)立馬被垃圾回收器所回收。
- 當(dāng)堆內(nèi)存的使用率接近閾值時(shí)祠锣,才會(huì)去回收軟引用的對(duì)象酷窥。
4. 弱引用
弱引用是一種比軟引用還要弱的引用類型。在系統(tǒng)進(jìn)行GC的時(shí)候伴网,只要發(fā)現(xiàn)弱引用都會(huì)對(duì)其進(jìn)行回收蓬推。我們可以通過java.lang.ref.WeakReference來保存一個(gè)弱引用的實(shí)例。
public class ReferenceTest {
public static void main(String[] args) {
byte[] byte1 = new byte[1024 * 515];
WeakReference<byte[]> weakReference = new WeakReference<>(byte1);
byte1 = null;
System.out.println("弱引用是否被回收 : "+weakReference.get());
System.gc();
System.out.println("弱引用是否被回收 : "+weakReference.get());
}
}
/**********************************************************************************************/
/**
弱引用是否被回收 : [B@7cd84586
弱引用是否被回收 : null
*/
5. 虛引用
虛引用是所有引用類型中最弱的一種澡腾,一個(gè)持有虛引用的對(duì)象和幾乎沒有引用是一樣的沸伏,隨時(shí)可能被垃圾回收器所回收糕珊。虛引用的get方法總是會(huì)返回null。為了追蹤垃圾回收過程毅糟,虛引用必須和引用隊(duì)列一起使用红选。
public class ReferenceTest {
public static void main(String[] args) {
byte[] byte1 = new byte[1024 * 515];
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
PhantomReference sf = new PhantomReference<>(byte1,referenceQueue);
byte1 = null;
System.out.println("是否被回收"+sf.get());
System.gc();
System.out.println("是否被回收"+sf.get());
}
}
/*********************************************************************************************/
/**
是否被回收null
是否被回收null
*/
6. WeakHashMap
WeakHashMap類屬于java.util包,實(shí)現(xiàn)了Map接口留特,是HashMap的一種實(shí)現(xiàn)纠脾,使用弱引用作為內(nèi)部數(shù)據(jù)的存儲(chǔ)方案。
/** -Xmx4m -Xms4m -Xmn4m 限定堆椡汕啵空間大小為4M*/
public class ReferenceTest {
public static void main(String[] args) {
// WeakHashMap<Object, Object> weakMap = new WeakHashMap<>();
// for (int i = 0 ; i<10000000; i++){
// weakMap.put(i, new byte[i]);
// }
HashMap<Object, Object> hashMap = new HashMap<>();
for (int i = 0 ; i<10000000; i++){
hashMap.put(i, new byte[i]);
}
}
}
通過結(jié)果我們可以發(fā)現(xiàn)苟蹈,使用WeakHashMap的代碼可以正常運(yùn)行結(jié)束,而使用HashMap的代碼則會(huì)拋出OOM異常右核,因?yàn)閃eakHashMap會(huì)在內(nèi)存使用緊張時(shí)使用弱引用慧脱,自動(dòng)釋放掉持有弱引用的內(nèi)存數(shù)據(jù),但是如果WeakHashMap的key都在系統(tǒng)里持有強(qiáng)引用贺喝,那么WeakHashMap就會(huì)跟普通的HashMap一樣菱鸥,里面的數(shù)據(jù)無法被自動(dòng)清理。