在java中提供4個級別的引用:強引用、軟引用、弱引用和虛引用。除了強引用外,其餘3中引用都可以在java.lang.ref包中找到對應的類。開發人員能夠在應用程序中直接使用他們。java
1 強引用緩存
強引用就是程序中通常使用的引用類型,強引用的對象是可觸及的,不會被回收。相對的,軟引用、弱引用和虛引用的對象是軟可觸及的、弱可觸及的和虛可觸及的,在必定條件下,都是能夠被回收的。jvm
強引用示例:StringBuffer str = new StringBuffer("Hello world");ide
假設以上代碼是在函數體內運行的,那麼局部變量str將被分配在棧上,而對象StringBuffer實例被分配在堆上。局部變量str指向StringBuffer實例所在堆空間,經過str能夠操做該實例,那麼str就是StringBuffer實例的強引用。函數
此時,若是再運行一個賦值語句:this
StringBuffer str1 = str;線程
則str所指向的對象也將被str1所指向,同時在局部變量表上會分配空間存放str1變量。此時該StringBuffer實例就有兩個引用。對引用的「==」操做用於表示兩操做數所指向的堆空間地址是否相同,不表示兩操做數所指的對象是否相同。對象
強引用的特色:blog
2 軟引用----可被回收的引用隊列
軟引用是比強引用弱一點的引用類型,一個對象只持有軟引用,那麼當堆空間不足時,就會被回收。軟引用使用java.lang.ref.SoftReference類型
package com.jvm;
import java.lang.ref.SoftReference;
public class SoftRef {
public static class User{
public User(int id,String name){
this.id = id;
this.name = name;
}
public int id;
public String name;
@Override
public String toString() {
return "[id="+String.valueOf(id)+",name="+name+"]";
}
}
public static void main(String[] args) {
User u = new User(1,"gim");
SoftReference<User> userSoftRef = new SoftReference<User>(u);//建立引用給定對象的新的軟引用
u = null; //去除強引用
System.out.println(userSoftRef.get());
System.gc();
System.out.println("After GC:");
System.out.println(userSoftRef.get());
byte[] b = new byte[1024*925*7];//分配7M左右,使內存空間緊張
System.gc();
System.out.println(userSoftRef.get());
}
}
若是虛擬機不加參數設置堆內存限制,則打印出:
[id=1,name=gim]
After GC:
[id=1,name=gim]
[id=1,name=gim]
由於虛擬機的內存總量是九十多兆,且虛擬機企圖使用的最大內存總量是這個的十幾倍。故不會出現資源緊張,也不會產生內存溢出現象。
若是使用-Xmx10m運行上述代碼,能夠獲得:
[id=1,name=gim](第一次從軟引用中獲取數據)
After GC:
[id=1,name=gim](GC沒有清除軟引用)
null(因爲內存緊張,軟引用被清除)
結論:GC未必會回收軟引用的對象,可是當內存資源緊張時,軟引用對象會被回收,因此軟引用對象不會引發內存溢出。
3 弱引用-----發現即回收
弱引用是一種比軟引用較弱的引用類型。在系統GC時,只要發現弱引用,無論系統堆空間使用狀況如何,都會將對象進行回收。可是因爲垃圾回收器的優先級一般很低,所以,並不必定能很快的發現持有弱引用的對象。在這種狀況下,弱引用對象能夠存在較長的時間,一旦一個弱引用對象被垃圾回收器回收,便會加入一個註冊的引用隊列(這一點和軟引用很像)。弱引用使用java.lang.ref.WeakReference類實現。
實例2:展現弱引用的特色
package com.jvm;
import java.lang.ref.WeakReference;
public class WeakRef {
public static class User{
public User(int id,String name){
this.id = id;
this.name = name;
}
public int id;
public String name;
@Override
public String toString() {
return "[id="+String.valueOf(id)+",name="+name+"]";
}
}
public static void main(String[] args) {
User u = new User(1,"gim");
WeakReference<User> userReference = new WeakReference<User>(u);
u = null;
System.out.println(userReference.get());
System.gc();
System.out.println("After GC:");
System.out.println(userReference.get());
}
}
不用設置虛擬機參數,直接運行代碼打印出:
[id=1,name=gim]
After GC:
null
能夠看書,GC以後,弱引用對象當即被清除。弱引用和軟引用同樣,在構造弱引用時,也能夠指定一個引用隊列,當弱引用對象被回收時,就會加入指定的引用隊列,經過這個隊列能夠跟蹤對象的回收狀況。
注意:軟引用、弱引用都很是適合來保存那些無關緊要的緩存數據(應用場景)。若是這麼作,當系統內存不足時,這些緩存數據會被回收,不會致使內存的溢出,而當內存資源充足時,這些緩存又能夠存在至關長的時間,從而起到加速系統的做用。
4 虛引用----對象回收跟蹤
一個持有虛引用的對象,和沒有引用幾乎同樣。隨時均可能被垃圾回收器回收,當試圖經過虛引用的get()方法取得強引用時,老是會失敗。
而且,虛引用必須和引用隊列一塊兒使用,它的做用主要是跟蹤垃圾回收過程
當垃圾回收器準備回收一個對象時,若是發現它還有虛引用,就會在回收對象後,將這個虛引用加入引用隊列,以通知引用程序對象的回收狀況。
實例3 :使用虛引用跟蹤一個可復活對象的回收
package com.jvm;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
public class TraceCanReliveObj {
public static TraceCanReliveObj obj; //能夠復活對象
static ReferenceQueue<TraceCanReliveObj> phantomQueue = null;
public static class CheckRefQueue extends Thread{
@Override
public void run() {
while(true){
if(phantomQueue!=null){
PhantomReference<TraceCanReliveObj> objt = null;
try{
objt = (PhantomReference<TraceCanReliveObj>) phantomQueue.remove();
}catch(InterruptedException e){
e.printStackTrace();
}
if(objt!=null){
System.out.println("TraceCanReliveObj is delete by GC");
}
}
}
}
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("CanReliveObj finalize called");
obj = this; //使得對象復活
}
@Override
public String toString() {
return "I am CanReliveObj";
}
public static void main(String[] args) throws InterruptedException {
Thread t = new CheckRefQueue();
t.setDaemon(true);//守護線程
t.start();
phantomQueue = new ReferenceQueue<TraceCanReliveObj>();
obj = new TraceCanReliveObj();
//構造了TraceCanReliveObj對象的虛引用,並指定了引用隊列。
PhantomReference<TraceCanReliveObj> phantomRef = new PhantomReference<TraceCanReliveObj>(obj, phantomQueue);
obj = null; //將強引用去除
System.gc(); //第一次進行GC,因爲對象可復活,GC沒法回收該對象。
Thread.sleep(1000);
if(obj==null){
System.out.println("obj 是 null");
}else{
System.out.println("obj 可用");
}
System.out.println("第二次GC");
obj = null;
System.gc(); //第二次進行GC,因爲finalize()只會被調用一次,所以第2次GC會回收對象,同時其引用隊列應該也會捕獲到對象的回收
Thread.sleep(1000);
if(obj==null){
System.out.println("obj 是 null");
}else{
System.out.println("obj 可用");
}
}
}
直接運行代碼打印:
CanReliveObj finalize called (對象復活)
obj 可用
第二次GC (第2次,對象沒法復活)
TraceCanReliveObj is delete by GC (引用隊列捕獲到對象被回收)
obj 是 null
因爲虛引用能夠跟蹤對象的回收時間,所以,也能夠將一些資源釋放操做放置在虛引用中執行和記錄。