解決內存洩露和OOM的問題
解合理的使用引用可以幫助垃圾回收器更好的管理Java內存
在可能造成內存洩露的場景下
oom
因為軟引用、弱引用、虛引用,不會和它關聯的對象造成引用,所以不會對和他們關聯的對象的生命周期產生影響。
特別注意,在世紀程序設計中一般很少使用弱引用與虛引用,使用軟用的情況較多,這是因為軟引用可以加速JVM對垃圾內存的回收速度,可以維護系統的運行安全,防止內存溢出(OutOfMemory)等問題的產生。
解強引用是我們在編程過程中使用的最簡單的引用,如代碼String s=”abc”中變量s就是字符串對象”abc”的一個強引用。任何被強引用指向的對象都不能被垃圾回收器回收,這些對象都是在程序中需要的。
解軟引用是用來描述一些有用但並不是必需的對象,在Java中用java.lang.ref.SoftReference類來表示。對於軟引用關聯著的對象,只有在內存不足的時候JVM才會回收該對象。因此,這一點可以很好地用來解決OOM的問題,並且這個特性很適合用來實現緩存:比如網頁緩存、圖片緩存等。
解軟引用可以和一個引用隊列(ReferenceQueue)聯合使用,如果軟引用所引用的對象被JVM回收,這個軟引用就會被加入到與之關聯的引用隊列中。
解弱引用也是用來描述非必需對象的,當JVM進行垃圾回收時,無論內存是否充足,都會回收被弱引用關聯的對象。在java中,用java.lang.ref.WeakReference類來表示。
解虛引用和前面的軟引用、弱引用不同,它並不影響對象的生命周期。在java中用java.lang.ref.PhantomReference類表示。如果一個對象與虛引用關聯,則跟沒有引用與之關聯一樣,在任何時候都可能被垃圾回收器回收。
解要注意的是,虛引用必須和引用隊列關聯使用,當垃圾回收器准備回收一個對象時,如果發現它還有虛引用,就會把這個虛引用加入到與之 關聯的引用隊列中。程序可以通過判斷引用隊列中是否已經加入了虛引用,來了解被引用的對象是否將要被垃圾回收。如果程序發現某個虛引用已經被加入到引用隊列,那麼就可以在所引用的對象的內存被回收之前采取必要的行動。
Demo2 { (String[] args) { String hello = String(); SoftReference sr = SoftReference(hello); hello = ; System..println(sr.()); String hello1 = String(); WeakReference<String> sr1 = WeakReference<String>(hello1); hello1 = ; System..println(sr1.()); System.gc(); System..println(sr1.()); ReferenceQueue<String> queue = ReferenceQueue<>(); String hello2 = String(); PhantomReference<String> pr = PhantomReference<>(hello2, queue); hello2 = ; System..println(pr.()); } }
解下面舉個例子,假如有一個應用需要讀取大量的本地圖片,如果每次讀取圖片都從硬盤讀取,則會嚴重影響性能,但是如果全部加載到內存當中,又有可能造成內存溢出,此時使用軟引用可以解決這個問題。
解設計思路是:用一個HashMap來保存圖片的路徑 和 相應圖片對象關聯的軟引用之間的映射關系,在內存不足時,JVM會自動回收這些緩存圖片對象所占用的空間,從而有效地避免了OOM的問題。在Android開發中對於大量圖片下載會經常用到。
下面這段代碼是摘自博客:
http://blog.csdn.net/arui319/article/details/8489451
..... Map<String, SoftReference<Bitmap>> imageCache = HashMap<String, SoftReference<Bitmap>>(); <br>.... (String path) { Bitmap bitmap = BitmapFactory.decodeFile(path); SoftReference<Bitmap> softBitmap = SoftReference<Bitmap>(bitmap); imageCache.put(path, softBitmap); } Bitmap (String path) { SoftReference<Bitmap> softBitmap = imageCache.(path); (softBitmap == ) { ; } Bitmap bitmap = softBitmap.(); bitmap; }
解當Java虛擬機(JVM)覺得內存不夠用的時候,會觸發垃圾回收操作(GC),清除無用的對象,釋放內存。可是如何判斷一個對象是否是垃圾呢?其中的一個方法是計算指向該對象的引用數量,如果引用數量為0,那麼該對象就為垃圾(Thread對象是例外),否則還有用處,不能被回收。但是如果把引用數為0的對象都回收了,還是不能滿足內存需求怎麼辦?Java把引用分為4種類型,垃圾回收器會嘗試回收只有弱引用的對象。
解按照一個對象的引用可達(Reachable)強度,由強到弱分為5類,如下:
強可達(Strong Reachable)
在一個線程內,無需引用直接可達,新創建的對象是強可達的。
軟可達(Soft Reachable)
不是強可達的,但是通過一個軟引用(SoftReference)可達。
弱可達(Soft Reachable)
既不是強可達也不是軟可達,但是通過一個弱引用(WeakReference)可達。
虛可達(Phantom Reachable)
既不是強可達,不是軟可達,也不是弱可達,但是通過一個虛引用(PhantomReference)可達。
不可達(Unreachable)
沒有任何引用指向對象。
URL:http://www.bianceng.cn/Programming/Java/201608/50377.htm
比較好、容易理解的是Java垃圾回收器會優先清理可達強度低的對象。另外有兩個重要的點:
強可達的一定不會被清理
JVM保證拋出out of memory之前,清理所有的軟引用對象
引用是java中非常重要的概念,理解它對寫出高質量的代碼非常重要,否則寫出的代碼都是各種內存洩露、oom卻不自知。而要想搞明白這個概念,需要對jvm有一定的了解,包括內存分配和垃圾回收。
局部變量不持有對外部對象(即外部對象分配的內存區域)的的引用,因為局部變量只在它的作用區域內有效。
成員變量不一定持有對外部對象((即外部對象分配的內存區域))的引用,只有當成員變量是內部類的時候才會出現這個問題,如果成員變量是定義在其他的文件中,不會產生內存洩露,要注意是否可能造成內存洩露的問題。內部類才會持有對外部類的引用。即:1 內部類對象的創建依賴於外部類對象;2 內部類對象持有指向外部類對象的引用。
引用的簡單理解:A a = new A(),java會在堆中分配一塊區域,然後讓位於棧中的引用a指向這塊區域。
其實垃圾回收機制關注的是堆中的分配的一塊區域是否還被其他引用變量指著,如果沒有人指著,就可以回收了。
String hello = String(); SoftReference sr = SoftReference(hello); hello = ; System..println(sr.());
如上代碼所示,hello指向堆中分配一塊內存,軟引用也指向了這塊內存,這是hello清除了這個指向,所以這塊內存區域目前只有軟引用指向他,這個時候這塊內存區域不會立即被回收,除非JVM需要內存才會被回收。
hello1 = (); WeakReference<> sr1 = WeakReference<>(hello1); hello1 = ; System.out.println(sr1.get());
如上代碼所示,hello1指向堆中分配一塊內存,軟引用也指向了這塊內存,這是hello1清除了這個指向,所以這塊內存區域目前只有弱引用指向他,當JVM進行垃圾回收時,無論內存是否充足,都會回收這塊內存區域。
ReferenceQueue ReferenceQueue(); hello2 (); PhantomReference pr PhantomReference(hello2, ); hello2 ; Systemoutprintln(prget());
如上代碼所示,hello2指向堆中分配一塊內存,軟引用也指向了這塊內存,這是hello2清除了這個指向,所以這塊內存區域目前只有虛引用指向他,而虛引用則跟沒有引用與之關聯一樣,這塊內存區域在任何時候都可能被垃圾回收器回收。
5. 以後我們再說對象是否被回收,建議說成分配的區域是否被回收,這樣更加直指問題的核心。