有哪些引用類型?
在JDK1.2版之後,Java對引用的對象進行了擴充,將引用分為強引用(Strongly Reference)、軟引用(Soft Reference)、弱引用(Weak Reference)和虛引用(Phantom Reference)4種,這4種引用強度依次減弱。
- 強引用是最傳統的 "引用" 的定義,是指在程序代碼之中普遍存在的引用賦值,即類似 "Object obj = new Object()" 這種引用關係。無論任何情況下,只要強引用關係還存在,還可達,垃圾收集器就永遠不會回收掉被引用的對象。
- 軟引用是用來描述一些還有用,但非必須的對象。只被軟引用關聯着的對象,在系統將要發生內存溢出異常前,會把這些對象列進回收範圍之中進行二次回收,如果這次回收還沒有足夠的內存,才會拋出內存溢出異常。在JDK1.2之後提供了SoftReference類來實現軟引用。
- 弱引用也是用來描述那些非必須對象,但是它的強度比軟引用更弱一些,被弱引用關聯的對象只能生存到下一次垃圾收集發生為止。當垃圾收集器開始工作,無論當前內存是否足夠,都會回收掉只被弱引用關聯的對象。在JDK1.2版本之後提供了WeakReference類來實現若引用。
- 虛引用也稱為 "幽靈引用" 或者 "幻影引用",它是最弱的一種引用關係。一個對象是否有虛引用的存在,完全不會對其生存時間構成影響,也無法通過虛引用來去的一個對象實例。為一個對象設置虛引用關聯的唯一目的只是為了能在這個對象被收集器回收時收到一個系統通知。在JDK1.2版之後提供了PhantomReference類來實現虛引用。
以上摘自《深入理解java虛擬機》
驗證
弱引用
思路
定義一個java對象,並且講該對象和ReferenceQueue放入WeakReference中,分別在gc前後打印WeakReference和WeakReference的引用對象,在gc後獲取隊列中WeakReference並打印。
預期
gc前,可以獲取到WeakReference中引用對象,隊列中的WeakReference為空
gc後,獲取WeakReference中引用對象為null,隊列中的WeakReference可以獲取到剛剛創建的WeakReference,拿到標識並且打印
代碼
Reference類
public class Reference {
private String name;
private Integer age;
public Reference(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Reference reference = (Reference) o;
return Objects.equals(name, reference.name) && Objects.equals(age, reference.age);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Reference{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
WeakReferenceTag類
public class WeakReferenceTag<T> extends WeakReference<T> {
private String tag;
public WeakReferenceTag(T referent, ReferenceQueue<? super T> q, String tag) {
super(referent, q);
this.tag = tag;
}
public String getTag() {
return tag;
}
public void setTag(String tag) {
this.tag = tag;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
WeakReferenceTag<?> that = (WeakReferenceTag<?>) o;
return Objects.equals(tag, that.tag);
}
@Override
public int hashCode() {
return Objects.hashCode(tag);
}
@Override
public String toString() {
return "WeakReferenceTag{" +
"tag='" + tag + '\'' +
'}';
}
}
運行類
public class WeakReferenceTest {
public static void main(String[] args) throws InterruptedException {
// 引用隊列
ReferenceQueue<Reference> referenceQueue = new ReferenceQueue<>();
// 創建弱引用
WeakReference<Reference> weakReference = new WeakReferenceTag<>(
new Reference("name", 13),
referenceQueue,
UUID.randomUUID().toString());
// gc前打印
System.out.printf("gc前打印reference對象: %s", weakReference);
System.out.println();
System.out.printf("gc前獲取引用對象:%s", weakReference.get());
System.out.println();
System.out.printf("gc前獲取隊列reference:%s", referenceQueue.poll());
System.out.println();
// gc
System.gc();
Thread.sleep(1000);
// gc後獲取對象
System.out.printf("gc後獲取引用對象:%s", weakReference.get());
System.out.println();
// 查看隊列是否收到提醒
java.lang.ref.Reference<? extends Reference> poll;
while (true) {
if ((poll = referenceQueue.poll()) != null) {
System.out.println("回收隊列收到提醒,打印reference:" + poll);
return;
} else {
System.out.println("隊列未收到回收提醒,繼續等待...");
Thread.sleep(500);
}
}
}
}
創建一個 Reference 對象,包含name、age屬性,創建軟引用對象並設置 Reference對象作為引用對象,這裏用到UUID作為 weakReference的一個屬性(擴展了一下WeakReference,方便隊列收到提醒後,打印標識)。
可以看到在第一次打印reference、reference引用對象時可以獲取到,並且打印,但是隊列打印時為空,説明此刻reference、reference引用對象是存活的,隊列是沒有收到gc後的提醒的。
隨後在第一次gc後,再次獲取引用對象為空,緊接着獲取reference引用對象時為null。循環獲取隊列後,隊列收到提醒,並且打印了weakReference標誌。
結論
WeakReference作為軟引用,其生命週期在下次gc前,垃圾收集器gc後,不管內存是否夠用,都會被清除掉,並且會講該弱引用對象發送到隊列中。