Java/Android中有四種引用類型,分別是:java
Strong reference - 強引用
Soft Reference - 軟引用
Weak Reference - 弱引用
Phantom Reference - 虛引用ide
不一樣的引用類型有着不一樣的特性,同時也對應着不一樣的使用場景。函數
1.Strong reference - 強引用oop
實際編碼中最多見的一種引用類型。常見形式如:A a = new A();等。強引用自己存儲在棧內存中,其存儲指向對內存中對象的地址。通常狀況下,當對內存中的對象再也不有任何強引用指向它時,垃圾回收機器開始考慮可能要對此內存進行的垃圾回收。如當進行編碼:a = null,此時,剛剛在堆中分配地址並新建的a對象沒有其餘的任何引用,當系統進行垃圾回收時,堆內存將被垃圾回收。this
SoftReference、WeakReference、PhantomReference都是類java.lang.ref.Reference的子類。Reference做爲抽象基類,定義了其子類對象的基本操做。Reference子類都具備以下特色:
1.Reference子類不能無參化直接建立,必須至少以強引用對象爲構造參數,建立各自的子類對象;
2.由於1中以強引用對象爲構造參數建立對象,所以,使得本來強引用所指向的堆內存中的對象將再也不只與強引用自己直接關聯,與Reference的子類對象的引用也有必定聯繫。且此種聯繫將可能影響到對象的垃圾回收。編碼
根據不一樣的子類對象對其指示對象(強引用所指向的堆內存中的對象)的垃圾回收不一樣的影響特色,分別造成了三個子類,即SoftReference、WeakReference和PhantomReference。spa
2.Soft Reference - 軟引用線程
軟引用的通常使用形式以下:
A a = new A();
SoftReference<A> srA = new SoftReference<A>(a);code
經過對象的強引用爲參數,建立了一個SoftReference對象,並使棧內存中的wrA指向此對象。對象
此時,進行以下編碼:a = null,對於本來a所指向的A對象的垃圾回收有什麼影響呢?
先直接看一下下面一段程序的輸出結果:
1 import java.lang.ref.SoftReference; 2
3 public class ReferenceTest { 4
5 public static void main(String[] args) { 6
7 A a = new A(); 8
9 SoftReference<A> srA = new SoftReference<A>(a); 10
11 a = null; 12
13 if (srA.get() == null) { 14 System.out.println("a對象進入垃圾回收流程"); 15 } else { 16 System.out.println("a對象還沒有被回收" + srA.get()); 17 } 18
19 // 垃圾回收
20 System.gc(); 21
22 if (srA.get() == null) { 23 System.out.println("a對象進入垃圾回收流程"); 24 } else { 25 System.out.println("a對象還沒有被回收" + srA.get()); 26 } 27
28 } 29 } 30
31 class A { 32
33 }
##輸出結果爲:
1 a對象還沒有被回收A@4807ccf6 2 a對象還沒有被回收A@4807ccf6
當 a = null後,堆內存中的A對象將再也不有任何的強引用指向它,但此時尚存在srA引用的對象指向A對象。當第一次調用srA.get()方法返回此指示對象時,因爲垃圾回收器頗有可能還沒有進行垃圾回收,此時get()是有結果的,這個很好理解。當程序執行System.gc();強制垃圾回收後,經過srA.get(),發現依然能夠獲得所指示的A對象,說明A對象並未被垃圾回收。那麼,軟引用所指示的對象何時纔開始被垃圾回收呢?須要知足以下兩個條件:
1.當其指示的對象沒有任何強引用對象指向它;
2.當虛擬機內存不足時。
所以,SoftReference變相的延長了其指示對象佔據堆內存的時間,直到虛擬機內存不足時垃圾回收器纔回收此堆內存空間。
3.Weak Reference - 弱引用
一樣的,軟引用的通常使用形式以下:
A a = new A();
WeakReference<A> wrA = new WeakReference<A>(a);
當沒有任何強引用指向此對象時, 其垃圾回收又具備什麼特性呢?
1 import java.lang.ref.WeakReference; 2
3 public class ReferenceTest { 4
5 public static void main(String[] args) { 6
7 A a = new A(); 8
9 WeakReference<A> wrA = new WeakReference<A>(a); 10
11 a = null; 12
13 if (wrA.get() == null) { 14 System.out.println("a對象進入垃圾回收流程"); 15 } else { 16 System.out.println("a對象還沒有被回收" + wrA.get()); 17 } 18
19 // 垃圾回收
20 System.gc(); 21
22 if (wrA.get() == null) { 23 System.out.println("a對象進入垃圾回收流程"); 24 } else { 25 System.out.println("a對象還沒有被回收" + wrA.get()); 26 } 27
28 } 29
30 } 31
32 class A { 33
34 }
##輸出結果爲:
a對象還沒有被回收A@52e5376a
a對象進入垃圾回收流程
輸出的第一條結果解釋同上。當進行垃圾回收後,wrA.get()將返回null,代表其指示對象進入到了垃圾回收過程當中。所以,對弱引用特色總結爲:
WeakReference不改變原有強引用對象的垃圾回收時機,一旦其指示對象沒有任何強引用對象時,此對象即進入正常的垃圾回收流程。
那麼,依據此特色,極可能有疑問:WeakReference存在又有什麼意義呢?
其主要使用場景見於:當前已有強引用指向強引用對象,此時因爲業務須要,須要增長對此對象的引用,同時又不但願改變此引用的垃圾回收時機,此時WeakReference正好符合需求,常見於一些與生命週期的場景中。
下面給出一個Android中關於WeakReference使用的場景 —— 結合靜態內部類和WeakReference來解決Activity中可能存在的Handler內存泄露問題。
Activity中咱們須要新建一個線程獲取數據,使用handler - sendMessage方式。下面是這一過程的通常性代碼:
1 public class MainActivity extends Activity { 2
3 //...
4 private int page; 5 private Handler handler = new Handler() { 6
7 @Override 8 public void handleMessage(Message msg) { 9 if (msg.what == 1) { 10
11 //...
12
13 page++; 14 } else { 15
16 //...
17
18 } 19
20 }; 21 }; 22
23 @Override 24 protected void onCreate(Bundle savedInstanceState) { 25 super.onCreate(savedInstanceState); 26 setContentView(R.layout.activity_main); 27
28 //...
29
30 new Thread(new Runnable() { 31 @Override 32 public void run() { 33 //..
34 Message msg = Message.obtain(); 35 msg.what = 1; 36 //msg.obj = xx;
37 handler.sendMessage(msg); 38 } 39 }).start(); 40
41 //...
42
43 } 44
45 }
在Eclispe中Run Link,將會看到警示信息:This Handler class should be static or leaks might occur ...點擊查看此信息,其詳情中對問題進行了說明並給出了建議性的解決方案。
Issue: Ensures that Handler classes do not hold on to a reference to an outer class Id: HandlerLeak Since this Handler is declared as an inner class, it may prevent the outer class from being garbage collected. If the Handler is using a Looper or MessageQueue for a thread other than the main thread, then there is no issue. If the Handler is using the Looper or MessageQueue of the main thread, you need to fix your Handler declaration, as follows: Declare the Handler as a static class;In the outer class, instantiate a WeakReference to the outer class and pass this object to your Handler when you instantiate the Handler; Make all references to members of the outer class using the WeakReference object.
大體的意思是建議將Handler定義成內部靜態類,並在此靜態內部類中定義一個WeakReference的引用,因爲指示外部的Activity對象。
問題分析:
Activity具備自身的生命週期,Activity中新開啓的線程運行過程當中,可能此時用戶按下了Back鍵,或系統內存不足等但願回收此Activity,因爲Activity中新起的線程並不會遵循Activity自己的什麼週期,也就是說,當Activity執行了onDestroy,因爲線程以及Handler 的HandleMessage的存在,使得系統本但願進行此Activity內存回收不能實現,由於非靜態內部類中隱性的持有對外部類的引用,致使可能存在的內存泄露問題。
所以,在Activity中使用Handler時,一方面須要將其定義爲靜態內部類形式,這樣可使其與外部類(Activity)解耦,再也不持有外部類的引用,同時因爲Handler中的handlerMessage通常都會多少須要訪問或修改Activity的屬性,此時,須要在Handler內部定義指向此Activity的WeakReference,使其不會影響到Activity的內存回收同時,能夠在正常狀況下訪問到Activity的屬性。
Google官方給出的建議寫法爲:
1 public class MainActivity extends Activity { 2
3 //...
4 private int page; 5 private MyHandler mMyHandler = new MyHandler(this); 6
7 private static class MyHandler extends Handler { 8
9 private WeakReference<MainActivity> wrActivity; 10
11 public MyHandler(MainActivity activity) { 12 this.wrActivity = new WeakReference<MainActivity>(activity); 13 } 14
15 @Override 16 public void handleMessage(Message msg) { 17 if (wrActivity.get() == null) { 18 return; 19 } 20 MainActivity mActivity = wrActivity.get(); 21 if (msg.what == 1) { 22
23 //...
24 mActivity.page++; 25
26 } else { 27
28 //...
29
30 } 31 } 32
33 } 34
35 @Override 36 protected void onCreate(Bundle savedInstanceState) { 37 super.onCreate(savedInstanceState); 38 setContentView(R.layout.activity_main); 39
40 //...
41
42 new Thread(new Runnable() { 43 @Override 44 public void run() { 45 //..
46 Message msg = Message.obtain(); 47 msg.what = 1; 48 //msg.obj = xx;
49 mMyHandler.sendMessage(msg); 50 } 51 }).start(); 52
53 //...
54
55 } 56
57 }
對於SoftReference和WeakReference,還有一個構造器參數爲ReferenceQueue<T>,當SoftReference或WeakReference所指示的對象確實被垃圾回收後,其引用將被放置於ReferenceQueue中。注意上文中,當SoftReference或WeakReference的get()方法返回null時,僅是代表其指示的對象已經進入垃圾回收流程,此時對象不必定已經被垃圾回收。而只有確認被垃圾回收後,若是ReferenceQueue,其引用纔會被放置於ReferenceQueue中。
看下面的一個例子:
1 public class ReferenceTest { 2
3 public static void main(String[] args) { 4
5 A a = new A(); 6
7 WeakReference<A> wrA = new WeakReference<A>(a); 8
9 a = null; 10
11 if (wrA.get() == null) { 12 System.out.println("a對象進入垃圾回收流程"); 13 } else { 14 System.out.println("a對象還沒有被回收" + wrA.get()); 15 } 16
17 // 垃圾回收
18 System.gc(); 19
20 if (wrA.get() == null) { 21 System.out.println("a對象進入垃圾回收流程"); 22 } else { 23 System.out.println("a對象還沒有被回收" + wrA.get()); 24 } 25
26 } 27 } 28
29 class A { 30
31 @Override 32 protected void finalize() throws Throwable { 33 super.finalize(); 34 System.out.println("in A finalize"); 35 } 36
37 }
##輸出結果爲:
1 a對象還沒有被回收A@46993aaa 2 a對象被回收 3 in A finalize
由此,也驗證了上文中的「進入垃圾回收流程」的說法。下面結合ReferenceQueue,看一段代碼:
1 public class ReferenceTest { 2
3 public static void main(String[] args) { 4
5 A a = new A(); 6
7 ReferenceQueue<A> rq = new ReferenceQueue<A>(); 8 WeakReference<A> wrA = new WeakReference<A>(a, rq); 9
10 a = null; 11
12 if (wrA.get() == null) { 13 System.out.println("a對象進入垃圾回收流程"); 14 } else { 15 System.out.println("a對象還沒有被回收" + wrA.get()); 16 } 17
18 System.out.println("rq item:" + rq.poll()); 19
20 // 垃圾回收
21 System.gc(); 22
23 if (wrA.get() == null) { 24 System.out.println("a對象進入垃圾回收流程"); 25 } else { 26 System.out.println("a對象還沒有被回收" + wrA.get()); 27 } 28
29 /*
30 try { 31 Thread.sleep(1000); 32 } catch (InterruptedException e) { 33 e.printStackTrace(); 34 } 35 */
36
37 System.out.println("rq item:" + rq.poll()); 38
39 } 40 } 41
42 class A { 43
44 @Override 45 protected void finalize() throws Throwable { 46 super.finalize(); 47 System.out.println("in A finalize"); 48 } 49
50 }
##輸出結果爲:
1 a對象還沒有被回收A@302b2c81 2 rq item:null 3 a對象進入垃圾回收流程 4 rq item:null 5 in A finalize
由此,驗證了「僅進入垃圾回收流程的SoftReference或WeakReference引用還沒有被加入到ReferenceQueue」。
1 public class ReferenceTest { 2
3 public static void main(String[] args) { 4
5 A a = new A(); 6
7 ReferenceQueue<A> rq = new ReferenceQueue<A>(); 8 WeakReference<A> wrA = new WeakReference<A>(a, rq); 9
10 a = null; 11
12 if (wrA.get() == null) { 13 System.out.println("a對象進入垃圾回收流程"); 14 } else { 15 System.out.println("a對象還沒有被回收" + wrA.get()); 16 } 17
18 System.out.println("rq item:" + rq.poll()); 19
20 // 垃圾回收
21 System.gc(); 22
23 if (wrA.get() == null) { 24 System.out.println("a對象進入垃圾回收流程"); 25 } else { 26 System.out.println("a對象還沒有被回收" + wrA.get()); 27 } 28
29 try { 30 Thread.sleep(1); 31 } catch (InterruptedException e) { 32 e.printStackTrace(); 33 } 34
35 System.out.println("rq item:" + rq.poll()); 36
37 } 38 } 39
40 class A { 41
42 @Override 43 protected void finalize() throws Throwable { 44 super.finalize(); 45 System.out.println("in A finalize"); 46 } 47
48 }
##輸出結果爲:
1 a對象還沒有被回收A@6276e1db 2 rq item:null 3 a對象進入垃圾回收流程 4 in A finalize 5 rq item:java.lang.ref.WeakReference@645064f
由此,證明了上述說法。
4.PhantomReference
與SoftReference或WeakReference相比,PhantomReference主要差異體如今以下幾點:
1.PhantomReference只有一個構造函數PhantomReference(T referent, ReferenceQueue<? super T> q),所以,PhantomReference使用必須結合ReferenceQueue;
2.無論有無強引用指向PhantomReference的指示對象,PhantomReference的get()方法返回結果都是null。
1 public class ReferenceTest { 2
3 public static void main(String[] args) { 4
5 A a = new A(); 6
7 ReferenceQueue<A> rq = new ReferenceQueue<A>(); 8 PhantomReference<A> prA = new PhantomReference<A>(a, rq); 9
10 System.out.println("prA.get():" + prA.get()); 11
12 a = null; 13
14 System.gc(); 15
16 try { 17 Thread.sleep(1); 18 } catch (InterruptedException e) { 19 e.printStackTrace(); 20 } 21
22 System.out.println("rq item:" + rq.poll()); 23
24 } 25 } 26
27 class A { 28
29 }
##輸出結果爲:
1 prA.get():null 2 rq item:java.lang.ref.PhantomReference@1da12fc0
代碼中的Thread.sleep(1);做用與上例中相同,都是確保垃圾回收線程可以執行。不然,進進入垃圾回收流程而沒有真正被垃圾回收的指示對象的虛引用是不會被加入到PhantomReference中的。
與WeakReference相同,PhantomReference並不會改變其指示對象的垃圾回收時機。且能夠總結出:ReferenceQueue的做用主要是用於監聽SoftReference/WeakReference/PhantomReference的指示對象是否已經被垃圾回收。