1、Java引用的種類
1、1 對象在內存中的狀態
class Node {
Node next;
String name;
public Node(String name) {
this.name = name;
}
}
public class NodeTest {
public static void main(String[] args) {
Node n1 = new Node("一號節點");
Node n2 = new Node("二號節點");
Node n3 = new Node("三號節點");
n1.next = n2;
n3 = n2;
n2 = null;
}
}
上面程序中定義了三個Node對象,並通過合適的引用關係把這三個Node對象組織在一起,下圖爲JVM中對應的有向圖。- 可達狀態:當一個對象被創建後,有一個以上的引用變量引用它。在有向圖可以從起始頂點導航到該對象,那麼它就處於可達狀態,程序可以通過引用變量來調用該對象的屬性和方法。
- 可恢復狀態:如果程序中某個對象不再有任何引用變量引用它,它將先進入可恢復狀態,此時從有向圖的起始頂點不能導航到該對象。在這種狀態下,系統的垃圾回收機制準備回收該對象所佔用的內存。如果系統調用finalize方法重新讓一個以上的引用變量引用該對象,則這個對象會再次變爲可達狀態;否則,該對象將進入不可達狀態。
- 不可達狀態:當對象的所有關聯都被切斷,且系統調用所有對象的finalize方法依然沒有使該對象變成可達狀態後,這個對象將永久性地失去引用,最後變成不可達狀態。只有當一個對象處於不可達狀態時,系統纔會真正回收該對象所佔有的資源。
public class StatusTranfer {
public static void test() {
String a = new String("Java對象1"); //①
a = new String("Java對象2"); //②
}
public static void main(String[] args) {
test();
}
}
當程序執行test方法的①行代碼時,代碼定義了a變量,並讓該變量指向“Java對象1”字符串。該代碼執行結束後,“Java對象1”字符串對象處於可達狀態。1、2 強引用
這是Java程序中最常見的引用方式,程序創建一個對象,並把這個對象賦給一個引用變量,這個引用變量就是強變量。當一個對象被一個或一個以上的強引用變量所引用時,它處於可達狀態,它不可能被系統垃圾回收機制回收。強引用時Java編程中廣泛使用的引用類型,被強引用所引用的Java對象絕不會被垃圾回收機制回收,即使內存非常緊張;即使有些Java對象以後永遠也不會被用到,JVM也不會回收強引用所引用的Java對象。1、3 軟引用
1、4 弱引用
弱引用和軟引用有點類似,區別在於弱引用所引用對象的生存期更短。對於只有弱引用的對象而言,當系統垃圾回收機制運行時,不管系統內存是否足夠,總會回收該對象所佔用的內存。但是並不是當一個對象只有弱引用時,它就會立即被回收,而是等到系統垃圾回收機制運行時纔會被回收。import java.lang.ref.WeakReference;
public class WeakReferenceTest {
public static void main(String[] args) {
String str = new String("Java對象");
WeakReference<String> wr = new WeakReference<String>(str);
str = null; //切斷str引用和"Java對象"字符之間的引用
System.out.println(wr.get()); //①
System.gc(); //強制垃圾回收
System.runFinalization();
System.out.println(wr.get()); //②
}
}
輸出結果爲:null
當執行str = null;之後,切斷了str和“Java對象”字符串對象之間的引用關係,此時只有一個弱引用對象引用字符串對象,程序中①行代碼依然可以輸出“Java對象”。如果系統垃圾回收機制啓動,只有弱引用的對象就會被清理掉。程序中②行代碼輸出null,這就表明對象已經被清理了。
1、5 虛引用
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
public class PhantomReferenceTest {
public static void main(String[] args) throws Exception {
String str = new String("Java對象");
ReferenceQueue<String> rq = new ReferenceQueue<>(); //創建引用隊列
PhantomReference<String> pr = new PhantomReference<String>(str, rq); //創建虛引用
str = null;
System.out.println(pr.get()); //①
System.gc(); //強制垃圾回收
System.runFinalization();
System.out.println(rq.poll() == pr); //取出引用隊列中最先進入隊列中的引用於pr進行比較 ②
}
}
輸出結果爲:true