簡介
首先大家應(yīng)該都知道Java從1.2起提供了四種引用類型,他們分別是其引用(StrongReference),軟引用(SoftReference),弱引用(WeakReference)以及PhantomReference(虛引用),他們被GC回收的可能性從大到小排列。如下圖
可以看到Reference是繼承自O(shè)bject,而又有三個直接的子類,就是我們要介紹的幾個類了。另外還有一個ReferenceQueue我想大家就不一定知道了

他是用來的保存即將釋放的引用對象,具體用戶下面有實例代碼
下面就來分別介紹每個引用,并附上測試代碼,方便大家的深入理解
StrongReference
就可以他的名字一樣,任何時候GC是不能回收他的,哪怕內(nèi)存不足時,系統(tǒng)會直接拋出異常OutOfMemoryError,也不會去回收,首先要說明的是java中默認(rèn)就是強引用,比如如下代碼:
Persion p = new Persion();
其中p就是一個強引用,任何時候都不會被gc回收
/**
* 測試強類型,可以看到我們調(diào)用多次gc他還是沒有回收
*/
private static void testStrongReference() {
Person jack = new Person("Jack");
System.gc();
System.gc();
System.out.println(jack);
}
SoftReference
軟引用他的特點是當(dāng)內(nèi)存足夠時不會回收這種引用類型的對象,只有當(dāng)內(nèi)存不夠用時才會回收,這種特點很適合最一些緩存,比如android中圖片的緩存
/**
* 測試軟類型引用
*/
private static void testSoftReference() {
Person jack = new Person("Jack");
SoftReference<Person> personSoftReference = new SoftReference<>(jack);
System.out.println(personSoftReference.get());
jack = null;
System.gc();
System.gc();
System.out.println(personSoftReference.get());
}
輸出:
Person{name='Jack'}
Person{name='Jack'}
可以看到雖然我們手動調(diào)用了GC但是引用類型沒有被回收
WeakReference
虛引用的特點是只要GC一運行就會把給回收了,個人感覺沒多大用處,因為只要GC一運行他就會被回收了‘
private static void testWeakReference() {
Person jack = new Person("Jack");
WeakReference<Person> personSoftReference = new WeakReference<Person>(jack);
System.out.println(personSoftReference.get());
System.gc();
System.gc();
System.out.println(personSoftReference.get());
}
我們運行代碼發(fā)現(xiàn)對象還是沒被回收,因為我們雖然調(diào)用了GC,這個方法只是通知他執(zhí)行,但是什么執(zhí)行還得看他心情。當(dāng)然上面的代碼有個很重要的原因是Jack對象他是強引用,所以在怎么調(diào)用GC他還是不會回收的,為啥呢?因為jack這個變量他還強引用著對象,我們給他置空,在調(diào)用GC他就回收了,如下代碼:
private static void testWeakReference() {
Person jack = new Person("Jack");
WeakReference<Person> personSoftReference = new WeakReference<Person>(jack);
System.out.println(personSoftReference.get());
jack = null;
System.gc();
System.gc();
System.out.println(personSoftReference.get());
}
輸出:
Person{name='Jack'}
null
可以看到結(jié)果正如我們所料
PhantomReference
虛引用就相應(yīng)沒引用似得,主要以創(chuàng)建這種類型的引用,那么他所引用類型就回收了
private static void testPhantomReference() {
Person jack = new Person("Jack");
ReferenceQueue<Person> personReferenceQueue = new ReferenceQueue<>();
PhantomReference<Person> personSoftReference = new PhantomReference<Person>(jack,personReferenceQueue);
System.out.println(personSoftReference.get());
// jack = null;
// System.gc();
// System.gc();
System.out.println(personSoftReference.get());
}
輸出:
null
null
可以看到只要一創(chuàng)建完就直接給回收了
ReferenceQueue
他是一個引用對列,可以監(jiān)控被回收的Reference對象,具體的就是當(dāng)一個引用對象他所引用的值被回收了,那么系統(tǒng)就會把這個引用添加到這個對列里面,如下代碼:
private static void testReferenceQueue() {
Person jack = new Person("Jack");
ReferenceQueue<Person> personReferenceQueue = new ReferenceQueue<>();
WeakReference<Person> personSoftReference = new WeakReference<Person>(jack, personReferenceQueue);
jack = null;
System.gc();
System.out.println(personSoftReference.get());
}
當(dāng)personSoftReference所引用的Jack對象回收了,系統(tǒng)就會把personSoftReference對象添加到personReferenceQueue里,具體有什么作用呢,想象下這種情景,一個hashMap他的key是一個byte[]數(shù)組,我們需要創(chuàng)建一萬個這樣的key將他添加到hashMap,這是如果機器的內(nèi)存小那么他就可以內(nèi)存不足,但是當(dāng)我們用一個引用來包裹這個key,然后在添加到hashMap就不會了:
private static void testReferenceQueue1() {
ReferenceQueue<Object> weakReferenceReferenceQueue = new ReferenceQueue<>();
int M1 = 1024;
Object o = new Object();
Map<WeakReference<byte[]>, Object> map = new HashMap<>();
//創(chuàng)建一個線程監(jiān)聽回收的對象
new Thread(new Runnable() {
@Override
public void run() {
try {
int cnt = 0;
WeakReference<byte[]> k;
while((k = (WeakReference) weakReferenceReferenceQueue.remove()) != null) {
System.out.println((cnt++) + "recycle:" + k+","+k.get());
System.out.println("map.size:" + map.size());
}
} catch(InterruptedException e) {
//結(jié)束循環(huán)
}
}
}).start();
for (int i = 0; i < 10000; i++) {
byte[] bytes = new byte[M1];
map.put(new WeakReference<>(bytes,weakReferenceReferenceQueue),o);
}
System.gc();
System.out.println("map.size:" + map.size());
}
輸出:
...
9997recycle:java.lang.ref.WeakReference@2a62b5bc,null
map.size:10000
9998recycle:java.lang.ref.WeakReference@27e47833,null
map.size:10000
9999recycle:java.lang.ref.WeakReference@18e8473e,null
map.size:10000
可以看到他回收了這么多對象,因為我們調(diào)用get()時返回的為null,但是問題來了,這時候map的size還是10000,這不是我們期望的,因為他所引用的對象都被回首了,那這個map里面的可以相當(dāng)于失效了,我們希望的是把reference對象也回收了,意思是map的size變成真實可用對象的size
private static void testReferenceQueue2() {
ReferenceQueue<Object> weakReferenceReferenceQueue = new ReferenceQueue<>();
int M1 = 1024;
Object o = new Object();
Map<WeakReference<byte[]>, Object> map = new HashMap<>();
//創(chuàng)建一個線程監(jiān)聽回收的對象
new Thread(new Runnable() {
@Override
public void run() {
try {
int cnt = 0;
WeakReference<byte[]> k;
while ((k = (WeakReference) weakReferenceReferenceQueue.remove()) != null) {
map.remove(k); //在這里我們移除被回收對象的引用
System.out.println((cnt++) + "recycle:" + k);
System.out.println("map.size:" + map.size());
}
} catch (InterruptedException e) {
//結(jié)束循環(huán)
}
}
}).start();
for (int i = 0; i < 10000; i++) {
byte[] bytes = new byte[M1];
map.put(new WeakReference<>(bytes, weakReferenceReferenceQueue), o);
}
System.gc();
System.out.println("map.size:" + map.size());
}
輸出:
9996recycle:java.lang.ref.WeakReference@1608bcbd
map.size:3
9997recycle:java.lang.ref.WeakReference@777c9dc9
map.size:2
9998recycle:java.lang.ref.WeakReference@535779e4
map.size:1
9999recycle:java.lang.ref.WeakReference@6f6745d6
map.size:0
可以看見被回收了的對象,我們將它的key也從map里面移除了
WeakHashMap
他的效果是和上面差不多,當(dāng)一個key的引用對象被回收時他會自動的移除這個key
我們將下表是偶數(shù)的值裝到了list,這樣做是不讓他回收這些對象
private static void testWeakHashMap() {
ReferenceQueue<Object> weakReferenceReferenceQueue = new ReferenceQueue<>();
int M1 = 1024;
Object o = new Object();
Map<byte[], Object> map = new WeakHashMap<>();
ArrayList<byte[]> bytes1 = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
byte[] bytes = new byte[M1];
if (i%2==0){
bytes1.add(bytes);
}
map.put(bytes, o);
bytes = null;
}
System.gc();
System.out.println("map.size:" + map.size());
}
輸出:
map.size:5000
可以看到正如我們上面說的,他會移除哪些被回收的key。
參考:http://www.iflym.com/index.php/java-programe/201407140001.html