java語言中爲對象的引用分爲了四個級別,分別爲 強引用 、軟引用、弱引用、虛引用。
WeakReference與SoftReference、HardReference、PhantomReference的區別
1.WeakReference與SoftReference都可以用來保存對象的實例引用,這兩個類與垃圾回收有關。
WeakReference是弱引用,其中保存的對象實例可以被GC回收掉。這個類通常用於在某處保存對象引用,而又不干擾該對象被GC回收,通常用於Debug、內存監視工具等程序中。因爲這類程序一般要求即要觀察到對象,又不能影響該對象正常的GC過程。最近在JDK的Proxy類的實現代碼中也發現了Weakrefrence的應用,Proxy會把動態生成的Class實例暫存於一個由Weakrefrence構成的Map中作爲Cache。
2.SoftReference是軟引用,它保存的對象實例,除非JVM即將OutOfMemory,否則不會被GC回收。這個特性使得它特別適合設計對象Cache。對於Cache,我們希望被緩存的對象最好始終常駐內存,但是如果JVM內存喫緊,爲了不發生OutOfMemoryError導致系統崩潰,必要的時候也允許JVM回收Cache的內存,待後續合適的時機再把數據重新Load到Cache中。這樣可以系統設計得更具彈性。
二、很多時候我們需要考慮Android平臺上的內存管理問題,Dalvik VM給每個進程都分配了一定量的可用堆內存,當我們處理一些耗費資源的操作時可能會產生OOM錯誤(OutOfMemoryError)這樣的異常,Android123觀察了下國內的類似Market客戶端設計,基本上都沒有采用很好的內存管理機制和緩存處理如果細心的網友可能發現Android
Market客戶端載入時,每個列表項的圖標是異步刷新顯示的,但當我們快速的往下滾動到一定數量比如50個,再往回滾動時可能我們看到了部分App的圖標又重新開始加載,當然這一過程可能是從SQLite數據庫中緩存的,但是在內存中已經通過類似SoftReference的方式管理內存。
在Java中內存管理,引用分爲四大類,強引用HardReference、弱引用WeakReference、軟引用SoftReference和虛引用PhantomReference。它們的區別也很明顯,HardReference對象是即使虛擬機內存喫緊拋出OOM也不會導致這一引用的對象被回收,而WeakReference等更適合於一些數量不多,但體積稍微龐大的對象,在這四個引用中,它是最容易被垃圾回收的,而我們對於顯示類似Android
Market中每個應用的App Icon時可以考慮使用SoftReference來解決內存不至於快速回收,同時當內存短缺面臨Java VM崩潰拋出OOM前時,軟引用將會強制回收內存,最後的虛引用一般沒有實際意義,僅僅觀察GC的活動狀態,對於測試比較實用同時必須和ReferenceQueue一起使用。對於一組數據,我們可以通過HashMap的方式來添加一組SoftReference對象來臨時保留一些數據,同時對於需要反覆通過網絡獲取的不經常改變的內容,可以通過本地的文件系統或數據庫來存儲緩存,希望給國內做App
Store這樣的客戶端一些改進建議。
WeakReference的一個測試程序:
- import java.lang.ref.WeakReference;
- public class WeakReferenceTest {
- /**
- * @param args
- */
- public static void main(String[] args) {
- A a = new A();
- a.str = "Hello, reference";
- WeakReference<A> weak = new WeakReference<A>(a);
- a = null;
- int i = 0;
- while (weak.get() != null) {
- System.out.println(String.format("Get str from object of WeakReference: %s, count: %d", weak.get().str, ++i));
- if (i % 10 == 0) {
- System.gc();
- System.out.println("System.gc() was invoked!");
- }
- try {
- Thread.sleep(500);
- } catch (InterruptedException e) {
- }
- }
- System.out.println("object a was cleared by JVM!");
- }
- }
程序運行結果:
- Get str from object of WeakReference: Hello, reference, count: 1
- Get str from object of WeakReference: Hello, reference, count: 2
- Get str from object of WeakReference: Hello, reference, count: 3
- Get str from object of WeakReference: Hello, reference, count: 4
- Get str from object of WeakReference: Hello, reference, count: 5
- Get str from object of WeakReference: Hello, reference, count: 6
- Get str from object of WeakReference: Hello, reference, count: 7
- Get str from object of WeakReference: Hello, reference, count: 8
- Get str from object of WeakReference: Hello, reference, count: 9
- Get str from object of WeakReference: Hello, reference, count: 10
- System.gc() was invoked!
- object a was cleared by JVM!
SoftReference的一個測試程序:
- import java.lang.ref.SoftReference;
- public class SoftReferenceTest {
- /**
- * @param args
- */
- public static void main(String[] args) {
- A a = new A();
- a.str = "Hello, reference";
- SoftReference<A> sr = new SoftReference<A>(a);
- a = null;
- int i = 0;
- while (sr.get() != null) {
- System.out.println(String.format("Get str from object of SoftReference: %s, count: %d", sr.get().str, ++i));
- if (i % 10 == 0) {
- System.gc();
- System.out.println("System.gc() was invoked!");
- }
- try {
- Thread.sleep(500);
- } catch (InterruptedException e) {
- }
- }
- System.out.println("object a was cleared by JVM!");
- }
- }
程序運行結果:
- Get str from object of SoftReference: Hello, reference, count: 1
- Get str from object of SoftReference: Hello, reference, count: 2
- Get str from object of SoftReference: Hello, reference, count: 3
- Get str from object of SoftReference: Hello, reference, count: 4
- Get str from object of SoftReference: Hello, reference, count: 5
- Get str from object of SoftReference: Hello, reference, count: 6
- Get str from object of SoftReference: Hello, reference, count: 7
- Get str from object of SoftReference: Hello, reference, count: 8
- Get str from object of SoftReference: Hello, reference, count: 9
- Get str from object of SoftReference: Hello, reference, count: 10
- System.gc() was invoked!
- Get str from object of SoftReference: Hello, reference, count: 11
- Get str from object of SoftReference: Hello, reference, count: 12
- Get str from object of SoftReference: Hello, reference, count: 13
- Get str from object of SoftReference: Hello, reference, count: 14
- Get str from object of SoftReference: Hello, reference, count: 15
- Get str from object of SoftReference: Hello, reference, count: 16
- Get str from object of SoftReference: Hello, reference, count: 17
- Get str from object of SoftReference: Hello, reference, count: 18
- Get str from object of SoftReference: Hello, reference, count: 19
- Get str from object of SoftReference: Hello, reference, count: 20
- System.gc() was invoked!
- Get str from object of SoftReference: Hello, reference, count: 21
- Get str from object of SoftReference: Hello, reference, count: 22
- Get str from object of SoftReference: Hello, reference, count: 23
- Get str from object of SoftReference: Hello, reference, count: 24
- Get str from object of SoftReference: Hello, reference, count: 25
- Get str from object of SoftReference: Hello, reference, count: 26
- Get str from object of SoftReference: Hello, reference, count: 27
- Get str from object of SoftReference: Hello, reference, count: 28
SoftReference比WeakReference生命力更強,當JVM的內存不喫緊時,即使引用的對象被置爲空了,Soft還可以保留對該對象的引用,此時的JVM內存池實際上還保有原來對象,只有當內存喫緊的情況下JVM纔會清除Soft的引用對象,並且會在未來重新加載該引用的對象。
而WeakReference則當清理內存池時會自動清理掉引用的對象。
下面只針對java中的弱引用進行一些分析
在分析弱引用之前,先闡述一個概念:什麼是對象可到達和對象不可到達狀態。
其實很簡單,我舉個例子:
現在有如下兩個類class A class B,在JVM上生成他們兩個類的實例分別爲 instance a instance b
有如下表達式:
A a = new A();
B b = new B();
兩個強引用對象就生成了,好吧,那麼這個時候我做一下修改:
A a = new A();
B b = new B(a);
B的默認構造函數上是需要一個A的實例作爲參數的,那麼這個時候 A和B就產生了依賴,也可以說a和b產生了依賴,我們再用一個接近內存結構的圖來表達:
a是對象A的引用,b是對象B的引用,對象B同時還依賴對象A,那麼這個時候我們認爲從對象B是可以到達對象A的。
於是我又修改了一下代碼
A a = new A();
B b = new B(a);
a = null;
A對象的引用a置空了,a不再指向對象A的地址,我們都知道當一個對象不再被其他對象引用的時候,是會被GC回收的,很顯然及時a=null,那麼A對象也是不可能被回收的,因爲B依然依賴與A,在這個時候,造成了內存泄漏!
那麼如何避免上面的例子中內存泄漏呢?
很簡單:
A a = new A();
B b = new B(a);
a = null;
b = null;
這個時候B對象再也沒有被任何引用,A對象只被B對象引用,儘管這樣,GC也是可以同時回收他們倆的,因爲他們處於不可到達區域。
弱引用來了!
A a = new A();
WeakReference wr = new WeakReference(a);
//B b = new B(a);
當 a=null ,這個時候A只被弱引用依賴,那麼GC會立刻回收A這個對象,這就是弱引用的好處!他可以在你對對象結構和拓撲不是很清晰的情況下,幫助你合理的釋放對象,造成不必要的內存泄漏!!