一 概念簡介
在JVM記憶體中,一個對象擁有不同的引用類型,那這個對象在進行垃圾回收時會被執行不同的操作,進而影響這個對象的生命周期
1.1 強引用
強引用是使用最普遍的引用,我們平時代碼中定義的引用都是強引用。如果一個對象具有強引用,垃圾回收器絕不會回收它,即使是記憶體空間不足時,Java虛拟機甯願抛出OutOfMemoryError錯誤,使程式異常終止,也不會靠随意回收具有強引用的對象來解決記憶體不足問題。
1.2 軟引用
如果一個對象隻具有軟引用,那就類似于可有可物的生活用品。如果記憶體空間足夠,垃圾回收器就不會回收它,如果記憶體空間不足了,就會回收這些對象的記憶體。隻要垃圾回收器沒有回收它,該對象就可以被程式使用。軟引用一般用于實作緩存對象的操作
軟引用可以和一個引用隊列(ReferenceQueue)聯合使用,如果軟引用所引用的對象被垃圾回收,Java虛拟機就會把這個軟引用加入到與之關聯的引用隊列中
1.3 弱引用
弱引用是比軟引用強度更低一種引用方式。如果一個對象隻具有弱引用,他的生命周期更加短暫,那麼當垃圾回收器線程在掃描他所管理的記憶體區域時,不管記憶體空間是否足夠,都會将這個對象回收。不過垃圾回收器線程優先級比較低,是以隻有弱引用的對象不一定會很快的被回收。
弱引用可以和一個引用隊列(ReferenceQueue)聯合使用,如果弱引用所引用的對象被垃圾回收,Java虛拟機就會把這個弱引用加入到與之關聯的引用隊列中。
1.4 虛引用
虛引用又叫做幽靈引用,就想他的名字一樣,虛引用是形同虛設的,也就是虛引用相當于沒有引用。如果一個對象僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收。虛引用主要用于跟蹤一個對象被垃圾回收的過程。
虛引用必須和引用隊列(ReferenceQueue)聯合使用(因為虛引用無法獲得引用對象,不配合引用隊列使用的話将毫無意義)。當垃圾回收器準備回收一個對象時,如果發現它還有虛引用,就會在回收對象的記憶體之前,把這個虛引用加入到與之關聯的引用隊列中。程式可以通過判斷引用隊列中是否已經加入了虛引用,來了解被引用的對象是否将要被垃圾回收。程式如果發現某個虛引用已經被加入到引用隊列,那麼就可以在所引用的對象的記憶體被回收之前采取必要的行動。
二 涉及的類
在java.lang.ref包中提供了三個類:SoftReference類、WeakReference類和PhantomReference類,他們分别對應軟引用、弱引用和虛引用。這是三個類的父類都是Reference類,他們的方法基本上都一樣。
2.1 軟引用(SoftReference)
2.1.1 構造器
// 1.生成一個指向refrent對象的軟引用
public SoftReference(T referent)
// 2.使用引用隊列
public SoftReference(T referent, ReferenceQueue<? super T> q)
2.1.2 如何獲得引用對象
// 通過get方法得到軟引用所指向的對象
public T get()
如果這個引用指向的對象已經被回收,那麼get( )将傳回null
2.2 弱引用(WeakReference)
2.2.1 構造器
// 1.生成一個指向refrent對象的弱引用
public WeakReference(T referent)
// 2.使用引用隊列
public WeakReference(T referent, ReferenceQueue<? super T> q)
2.2.2 如何獲得引用對象
// 通過get方法得到弱引用所指向的對象
public T get()
如果這個引用指向的對象已經被回收,那麼get( )将傳回null
2.3 虛引用(PhantomReference)
2.3.1 構造器
// 使用引用隊列(虛引用必須配合引用隊列使用,否則沒有意義)
public WeakReference(T referent, ReferenceQueue<? super T> q)
2.3.2 如何獲得引用對象
// 通過get方法得到null?
public T get() {
return null;
}
虛引用不會獲得對象的引用,是以重載了Reference的get方法,直接傳回null。
2.4 引用隊列
public class ReferenceQueue<T> {
public Reference<? extends T> poll() //将隊列頂端的元素抛出
public Reference<? extends T> remove() // 将隊列元素删除
}
三 測試demo
3.1 測試程式
package com.example.demo.test;
import java.lang.ref.*;
import java.util.LinkedList;
public class ReferenceTest {
private static ReferenceQueue<Source> rq = new ReferenceQueue<Source>();
public static void checkQueue() {
Reference<? extends Source> ref = null;
while ((ref = rq.poll()) != null) {
if (ref != null) {
if(ref instanceof SourceSoftReference) {
System.out.println("In queue: " + ((SourceSoftReference) (ref)).id);
} else if(ref instanceof SourceWeakReference) {
System.out.println("In queue: " + ((SourceWeakReference) (ref)).id);
} else {
System.out.println("In queue: " + ((SourcePhantomReference) (ref)).id);
}
}
}
}
public static void main(String args[]) {
// soft();
// weak();
phantom();
}
public static void soft() {
int size = ;
LinkedList<SourceSoftReference> softList = new LinkedList<SourceSoftReference>();
for (int i = ; i < size; i++) {
softList.add(new SourceSoftReference(new Source("Soft " + i), rq));
System.out.println("Just created soft: " + softList.getLast());
}
System.gc();
try { // 下面休息幾分鐘,讓上面的垃圾回收線程運作完成
Thread.currentThread().sleep();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = ; i < size; i++) {
System.out.println("output: " + softList.get(i).get());
}
checkQueue();
}
public static void weak() {
int size = ;
LinkedList<SourceWeakReference> weakList = new LinkedList<SourceWeakReference>();
for (int i = ; i < size; i++) {
weakList.add(new SourceWeakReference(new Source("Weak " + i), rq));
System.out.println("Just created weak: " + weakList.getLast());
}
System.gc();
try { // 下面休息幾分鐘,讓上面的垃圾回收線程運作完成
Thread.currentThread().sleep();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = ; i < size; i++) {
System.out.println("output: " + weakList.get(i).get());
}
checkQueue();
}
public static void phantom() {
int size = ;
LinkedList<SourcePhantomReference> phantomList = new LinkedList<SourcePhantomReference>();
for (int i = ; i < size; i++) {
phantomList.add(new SourcePhantomReference(new Source("Phantom " + i), rq));
System.out.println("Just created phantom: " + phantomList.getLast());
}
System.gc();
try { // 下面休息幾分鐘,讓上面的垃圾回收線程運作完成
Thread.currentThread().sleep();
} catch (InterruptedException e) {
e.printStackTrace();
}
// System.gc();
// try { // 下面休息幾分鐘,讓上面的垃圾回收線程運作完成
// Thread.currentThread().sleep(2000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
for (int i = ; i < size; i++) {
System.out.println("output: " + phantomList.get(i).get());
}
checkQueue();
}
}
class Source {
public String id;
public Source(String id) {
this.id = id;
}
protected void finalize() {
System.out.println("Finalizing Source " + id);
}
}
class SourceSoftReference extends SoftReference<Source> {
public String id;
public SourceSoftReference(Source Source, ReferenceQueue<Source> rq) {
super(Source, rq);
this.id = Source.id;
}
}
class SourceWeakReference extends WeakReference<Source> {
public String id;
public SourceWeakReference(Source Source, ReferenceQueue<Source> rq) {
super(Source, rq);
this.id = Source.id;
}
}
class SourcePhantomReference extends PhantomReference<Source> {
public String id;
public SourcePhantomReference(Source Source, ReferenceQueue<Source> rq) {
super(Source, rq);
this.id = Source.id;
}
}
3.2 分析
3.2.1 運作soft()方法
輸出:
Just created soft: [email protected]
Just created soft: [email protected]
Just created soft: [email protected]
output: [email protected]
output: [email protected]
output: [email protected]
軟引用仍然能獲得對象的引用,且引用隊列為空,說明記憶體充足,對象未被回收
3.2.2 運作weak()方法
Just created weak: [email protected]
Just created weak: [email protected]
Just created weak: [email protected]
Finalizing Source Weak 2
Finalizing Source Weak 1
Finalizing Source Weak 0
output: null
output: null
output: null
In queue: Weak 1
In queue: Weak 2
In queue: Weak 0
弱引用無法獲得對象的引用,且引用隊列中有3個弱引用,說明弱引用指向的對象已經被回收
3.2.2 運作phantom()方法
Just created phantom: [email protected]
Just created phantom: [email protected]
Just created phantom: [email protected]
Finalizing Source Phantom 2
Finalizing Source Phantom 1
Finalizing Source Phantom 0
output: null
output: null
output: null
虛引用無法獲得對象的引用屬于正常現象,但是引用隊列為空不正常。這是為什麼呢?
問題出在了source對象的finalize方法上,注釋掉finalize方法,再運作結果如下:
Just created phantom: [email protected]
Just created phantom: [email protected]
Just created phantom: [email protected]
output: null
output: null
output: null
In queue: Phantom 2
In queue: Phantom 0
In queue: Phantom 1
這是為什麼???????
原因簡單叙述如下:
在垃圾回收時:
弱引用:一旦探測對象隻有弱引用,就會被插入到ReferenceQueue
虛引用:隻有對象确實被GC銷毀,才會被插入到ReferenceQueue
也就是說上例中虛引用指向的對象并沒有被銷毀
為什麼執行了 System.gc()後對象沒有被銷毀??
因為對象重載了finalize方法,需要執行2輪gc才能回收。
如何修改
把phantom()方法的注釋解開,執行2次GC後,輸出如我們想象中一樣
Just created phantom: [email protected]
Just created phantom: [email protected]
Just created phantom: [email protected]
Finalizing Source Phantom 0
Finalizing Source Phantom 2
Finalizing Source Phantom 1
output: null
output: null
output: null
In queue: Phantom 1
In queue: Phantom 0
In queue: Phantom 2
三 總結
不知道大家發現沒有,通過虛引用的demo,我們不難發現,弱引用的demo中的Source對象也未必被回收掉了(一輪gc肯定不會被回收掉)。
是以,慎重重寫finalize方法,它會延緩對象被銷毀的速度。