關於Android內存泄漏的種種總結第二彈

銜接上篇:
新年過後獻上關於Android內存泄漏的種種總結
順手留下GitHub鏈接,需要獲取相關面試等內容的可以自己去找
https://github.com/xiangjiana/Android-MS

更多完整項目下載。未完待續。源碼。圖文知識後續上傳github。
可以點擊關於我聯繫我獲取

在Android應用的開發中,爲了防止內存溢出,在處理一些佔用內存大而且聲明周 期較長的對象時候,可以儘量應用軟引用和弱引用技術。

軟/弱引用可以和一個引用隊列(ReferenceQueue)聯合使用,如果軟引用所引用 的對象被垃圾回收器回收,Java虛擬機就會把這個軟引用加入到與之關聯的引用隊 列中。利用這個隊列可以得知被回收的軟/弱引用的對象列表,從而爲緩衝器清除已 失效的軟/弱引用。

假設我們的應用會用到大量的默認圖片,比如應用中有默認的頭像,默認遊戲圖標 等等,這些圖片很多地方會用到。如果每次都去讀取圖片,由於讀取文件需要硬件 操作,速度較慢,會導致性能較低。所以我們考慮將圖片緩存起來,需要的時候直 接從內存中讀取。但是,由於圖片佔用內存空間比較大,緩存很多圖片需要很多的 內存,就可能比較容易發生OutOfMemory異常。這時,我們可以考慮使用軟/弱引 用技術來避免這個問題發生。

以下就是高速緩衝器的雛形: 首先定義一個HashMap,保存軟引用對象。

  private Map <String, SoftReference<Bitmap>> imageCache = new Has 
  hMap <String, SoftReference<Bitmap>> ();

再來定義一個方法,保存Bitmap的軟引用到HashMap

使用軟引用以後,在OutOfMemory異常發生之前,這些緩存的圖片資源的內存空間 可以被釋放掉的,從而避免內存達到上限,避免Crash發生。 如果只是想避免OutOfMemory異常的發生,則可以使用軟引用。

如果對於應用的性 能更在意,想盡快回收一些佔用內存比較大的對象,則可以使用弱引用。

另外可以根據對象是否經常使用來判斷選擇軟引用還是弱引用。如果該對象可能會 經常使用的,就儘量用軟引用。如果該對象不被使用的可能性更大些,就可以用弱 引用

ok,繼續回到主題。前面所說的,創建一個靜態Handler內部類,然後對 Handler 持有的對象使用弱引用,這樣在回收時也可以回收 Handler 持有的對象,但是這樣 做雖然避免了 Activity 泄漏,不過 Looper 線程的消息隊列中還是可能會有待處理的 消息,所以我們在 Activity 的 Destroy 時或者 Stop 時應該移除消息隊列 MessageQueue中的消息。

下面幾個方法都可以移除 Message:

  public final void removeCallbacks(Runnable r); 
  public final void removeCallbacks(Runnable r, Object token); 
  public final void removeCallbacksAndMessages(Object token); 
  public final void removeMessages(int what); 
  public final void removeMessages(int what, Object object);
  • 儘量避免使用 static 成員變量
    如果成員變量被聲明爲 static,那我們都知道其生命週期將與整個app進程生命 週期一樣。

    這會導致一系列問題,如果你的app進程設計上是長駐內存的,那即使app切到 後臺,這部分內存也不會被釋放。按照現在手機app內存管理機制,佔內存較 大的後臺進程將優先回收,因爲如果此app做過進程互保保活,那會造成app在 後臺頻繁重啓。當手機安裝了你參與開發的app以後一夜時間手機被消耗空了 電量、流量,你的app不得不被用戶卸載或者靜默。 這裏修復的方法是:

不要在類初始時初始化靜態成員。可以考慮lazy初始化。 架構設計上要思考是否真 的有必要這樣做,儘量避免。如果架構需要這麼設計,那麼此對象的生命週期你有 責任管理起來。

  • 避免 override finalize()
    1、finalize 方法被執行的時間不確定,不能依賴與它來釋放緊缺的資源。時間 不確定的原因是:
  • 虛擬機調用GC的時間不確定
  • Finalize daemon線程被調度到的時間不確定

2、finalize 方法只會被執行一次,即使對象被複活,如果已經執行過了 finalize 方法,再次被 GC 時也不會再執行了,原因是:
含有 finalize 方法的 object 是在 new 的時候由虛擬機生成了一個 finalize reference 在來引用到該Object的,而在 finalize 方法執行的時候,該 object 所 對應的 finalize Reference 會被釋放掉,即使在這個時候把該 object 復活(即用 強引用引用住該 object ),再第二次被 GC 的時候由於沒有了 finalize reference 與之對應,所以 finalize 方法不會再執行。

3、含有Finalize方法的object需要至少經過兩輪GC纔有可能被釋放。

  • 資源未關閉造成的內存泄漏
    對於使用了BraodcastReceiverContentObserver,File,遊標 Cursor, Stream,Bitmap等資源的使用,應該在Activity銷燬時及時關閉或者註銷,否 則這些資源將不會被回收,造成內存泄漏。
  • 一些不良代碼造成的內存壓力
    有些代碼並不造成內存泄露,但是它們,或是對沒使用的內存沒進行有效及時 的釋放,或是沒有有效的利用已有的對象而是頻繁的申請新內存。

比如:
構造 Adapter 時,沒有使用緩存的 convertView ,每次都在創建新的 converView。這裏推薦使用 ViewHolder

總結:

  • 對 Activity 等組件的引用應該控制在 Activity 的生命週期之內; 如果不能就考 慮使用 getApplicationContext 或者 getApplication,以避免 Activity 被外部長 生命週期的對象引用而泄露。
  • 儘量不要在靜態變量或者靜態內部類中使用非靜態外部成員變量(包括context ),即使要使用,也要考慮適時把外部成員變量置空;也可以在內部類中使用弱 引用來引用外部類的變量。
  • 對於生命週期比Activity長的內部類對象,並且內部類中使用了外部類的成員變 量,可以這樣做避免內存泄漏:
    • 將內部類改爲靜態內部類
    • 靜態內部類中使用弱引用來引用外部類的成員變量
  • Handler 的持有的引用對象最好使用弱引用,資源釋放時也可以清空 Handler 裏面的消息。比如在 Activity onStop 或者 onDestroy 的時候,取消掉該 Handler對象的 MessageRunnable.
  • 在 Java 的實現過程中,也要考慮其對象釋放,最好的方法是在不使用某對象 時,顯式地將此對象賦值爲 null,比如使用完Bitmap 後先調用 recycle(),再賦 爲null,清空對圖片等資源有直接引用或者間接引用的數組(使用 array.clear() ; array = null)等,最好遵循誰創建誰釋放的原則。
  • 正確關閉資源,對於使用了BraodcastReceiverContentObserver,File,遊 標 Cursor,Stream,Bitmap等資源的使用,應該在Activity銷燬時及時關閉或 者註銷。
  • 保持對對象生命週期的敏感,特別注意單例、靜態對象、全局性集合等的生命 週期。

刪減了一部分,見諒_
順手留下GitHub鏈接,需要獲取相關面試等內容的可以自己去找
https://github.com/xiangjiana/Android-MS

更多完整項目下載。未完待續。源碼。圖文知識後續上傳github。
可以點擊關於我聯繫我獲取

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章