java中引用的概念
強引用(StrongReference)
強引用就是指在程式程式碼之中普遍存在的,比如下面這段程式碼中的object和str都是強引用:
1 2 |
Object object =
new
Object();
String str =
"hello"
;
|
只要某個物件有強引用與之關聯,JVM必定不會回收這個物件,即使在記憶體不足的情況下,JVM寧願丟擲OutOfMemory錯誤也不會回收這種物件。比如下面這段程式碼:
1 2 3 4 5 6 7 8 9 10 |
public
class
Main {
public
static
void
main(String[] args) {
new Main().fun1();
}
public
void
fun1() {
Object object =
new
Object();
Object[] objArr =
new
Object[
1000
];
}
}
|
當執行至Object[] objArr = new Object[1000];這句時,如果記憶體不足,JVM會丟擲OOM錯誤也不會回收object指向的物件。不過要注意的是,當fun1執行完之後,object和objArr都已經不存在了,所以它們指向的物件都會被JVM回收。
如果想中斷強引用和某個物件之間的關聯,可以顯示地將引用賦值為null,這樣一來的話,JVM在合適的時間就會回收該物件。
軟引用(SoftReference)
軟引用是用來描述一些有用但並不是必需的物件,在Java中用java.lang.ref.SoftReference類來表示。對於軟引用關聯著的物件,只有在記憶體不足的時候JVM才會回收該物件。因此,這一點可以很好地用來解決OOM的問題,並且這個特性很適合用來實現快取:比如網頁快取、圖片快取等。
軟引用可以和一個引用佇列(ReferenceQueue)聯合使用,如果軟引用所引用的物件被JVM回收,這個軟引用就會被加入到與之關聯的引用佇列中。下面是一個使用示例:
1 2 3 4 5 6 7 8 9 10 11 12 |
import
java.lang.ref.WeakReference;
public
class
Main {
public
static
void
main(String[] args) {
SoftReference<String> sr =
new
SoftReference<String>(
new
String(
"hello"
));
System.out.println(sr.get());
System.gc();
//通知JVM的gc進行垃圾回收
System.out.println(sr.get());
}
}
|
弱引用(WeakReference)
弱引用也是用來描述非必需物件的,當JVM進行垃圾回收時,無論記憶體是否充足,都會回收被弱引用關聯的物件。在java中,用java.lang.ref.WeakReference類來表示。下面是使用示例:
1 2 3 4 5 6 7 8 9 10 11 12 |
import
java.lang.ref.WeakReference;
public
class
Main {
public
static
void
main(String[] args) {
WeakReference<String> sr =
new
WeakReference<String>(
new
String(
"hello"
));
System.out.println(sr.get());
System.gc();
//通知JVM的gc進行垃圾回收
System.out.println(sr.get());
}
}
|
虛引用(PhantomReference)
虛引用和前面的軟引用、弱引用不同,它並不影響物件的生命週期。在java中用java.lang.ref.PhantomReference類表示。如果一個物件與虛引用關聯,則跟沒有引用與之關聯一樣,在任何時候都可能被垃圾回收器回收。
要注意的是,虛引用必須和引用佇列關聯使用,當垃圾回收器準備回收一個物件時,如果發現它還有虛引用,就會把這個虛引用加入到與之 關聯的引用佇列中。程式可以通過判斷引用佇列中是否已經加入了虛引用,來了解被引用的物件是否將要被垃圾回收。如果程式發現某個虛引用已經被加入到引用佇列,那麼就可以在所引用的物件的記憶體被回收之前採取必要的行動。虛引用與軟引用和弱引用不同的是虛引用是在引用物件gc前加入佇列,所以可以在這一段時間對引用物件進行清理操作。
進一步理解軟引用和弱引用
對於強引用,我們平時在編寫程式碼時經常會用到。而對於其他三種類型的引用,使用得最多的就是軟引用和弱引用,這2種既有相似之處又有區別。它們都是用來描述非必需物件的,但是被軟引用關聯的物件只有在記憶體不足時才會被回收,而被弱引用關聯的物件在JVM進行垃圾回收時總會被回收。針對上面的特性,軟引用適合用來進行快取,當記憶體不夠時能讓JVM回收記憶體,弱引用能用來在回撥函式中防止記憶體洩露。因為回撥函式往往是匿名內部類,隱式儲存有對外部類的引用,所以如果回撥函式是在另一個執行緒裡面被回撥,而這時如果需要回收外部類,那麼就會記憶體洩露,因為匿名內部類儲存有對外部類的強引用。
public static boolean isRun = true; @SuppressWarnings("static-access") public static void main(String[] args) throws Exception { String abc = new String("abc"); System.out.println(abc.getClass() + "@" + abc.hashCode()); final ReferenceQueue<String> referenceQueue = new ReferenceQueue<String>(); new Thread() { public void run() { while (isRun) { Object obj = referenceQueue.poll(); if (obj != null) { try { Field rereferent = Reference.class .getDeclaredField("referent"); rereferent.setAccessible(true); Object result = rereferent.get(obj); System.out.println(obj); System.out.println("gc will collect:" + result.getClass() + "@" + result.hashCode() + "\t" + (String) result); } catch (Exception e) { e.printStackTrace(); } } } } }.start(); PhantomReference<String> abcWeakRef = new PhantomReference<String>(abc, referenceQueue); abc = null; Thread.currentThread().sleep(3000); System.gc(); Thread.currentThread().sleep(3000); isRun = false; }}