Android內存泄露相關

內存泄露原因

由於某些資源應該被釋放卻沒有被釋放,一直被某些對象佔用,導致GC不能正常回收資源。

JVM內存分配策略

java運行時的內存分配策略有:靜態分配、棧式分配、堆式分配。

不同的分配策略對應不同的內存區:

靜態內存區:主要存放靜態數據、全局 static 數據和常量。這塊內存在程序編譯時就已經分配好,並且在程序整個運行期間都存在。

棧區:當方法被執行時,方法體內的局部變量都在棧上創建,並在方法執行結束時這些局部變量所持有的內存將會自動被釋放。因爲棧內存分配運算內置於處理器的指令集中,效率很高,但是分配的內存容量有限。 

堆區:又稱動態內存分配,通常就是指在程序運行時直接 new 出來的內存。這部分內存在不使用時將會由 Java 垃圾回收器來負責回收。

局部變量的基本數據類型和引用存儲於棧中,引用的對象實體存儲於堆中。—— 因爲它們屬於方法中的變量,生命週期隨方法而結束。 

成員變量全部存儲與堆中(包括基本數據類型,引用和引用的對象實體)—— 因爲它們屬於類,類對象終究是要被new出來使用的。 

Java的內存管理

Java的內存管理,就是內存的分配與釋放問題。

分配是程序中通過new等關鍵字獲取的,釋放是GC做的。JVM需要維護一套所有對象的引用關係,所以JVM的運行速度會有些慢。監視對象狀態是爲了更加準確地、及時地釋放對象,而釋放對象的根本原則就是該對象不再被引用。

常見的泄露類型

1、集合類泄露:只add,沒有進行remove

2、單例:與應用生命週期同樣長,使用不當會造成泄露。單例時傳入的context應該是applicationcontext,如果是activitycontext,會造成內存泄露。

3、非靜態內部類:靜態內部類會持有外部類的引用。應該將非靜態內部類抽出來。

4、匿名內部類 :被異步線程持有。

5、Handler 造成的內存泄漏:很多時候我們爲了避免 ANR 而不在主線程進行耗時操作,在處理網絡任務或者封裝一些請求回調等api都藉助Handler來處理,但 Handler 不是萬能的,對於 Handler 的使用代碼編寫一不規範即有可能造成內存泄漏。另外,我們知道 Handler、Message 和 MessageQueue 都是相互關聯在一起的,萬一 Handler 發送的 Message 尚未被處理,則該 Message 及發送它的 Handler 對象將被線程 MessageQueue 一直持有。 

由於 Handler 屬於 TLS(Thread Local Storage) 變量, 生命週期和 Activity 是不一致的。因此這種實現方式一般很難保證跟 View 或者 Activity 的生命週期保持一致,故很容易導致無法正確釋放。 

6、儘量避免使用 static 成員變量

綜述即推薦使用靜態內部類 + WeakReference 這種方式。每次使用前注意判空。 
前面提到了 WeakReference,所以這裏就簡單的說一下 Java 對象的幾種引用類型。 

Java對引用的分類有 Strong reference, SoftReference, WeakReference, PhatomReference 四種

7、儘量避免使用 static 成員變量

以上,參考https://blog.csdn.net/u013495603/article/details/50696170

檢測工具

  1. 功能強大PC端檢測工具,如MemoryAnalyzer運行在PC端抓取Android手機中的dump文件進行深度分析。 http://www.eclipse.org/mat/
  2. 小而優的Android端檢測工具,如LeakCanary隨App一起安裝會在Android手機桌面安裝的內存泄露檢測App  https://www.liaohuqiu.net/cn/posts/leak-canary-read-me/

編程注意點:

  • 對 Activity 等組件的引用應該控制在 Activity 的生命週期之內; 如果不能就考慮使用 
    getApplicationContext 或者 getApplication,以避免 Activity 
    被外部長生命週期的對象引用而泄露。
  • 儘量不要在靜態變量或者靜態內部類中使用非靜態外部成員變量(包括context 
    ),即使要使用,也要考慮適時把外部成員變量置空;也可以在內部類中使用弱引用來引用外部類的變量。
  • 對於生命週期比Activity長的內部類對象,並且內部類中使用了外部類的成員變量,可以這樣做避免內存泄漏: 
    將內部類改爲靜態內部類

    靜態內部類中使用弱引用來引用外部類的成員變量

  • Handler 的持有的引用對象最好使用弱引用,資源釋放時也可以清空 Handler 裏面的消息。比如在 Activity onStop 
    或者 onDestroy 的時候,取消掉該 Handler 對象的 Message和 Runnable.

  • 在 Java 的實現過程中,也要考慮其對象釋放,最好的方法是在不使用某對象時,顯式地將此對象賦值爲 null,比如使用完Bitmap 
    後先調用 recycle(),再賦爲null,清空對圖片等資源有直接引用或者間接引用的數組(使用 array.clear() ; 
    array = null)等,最好遵循誰創建誰釋放的原則。

  • 正確關閉資源,對於使用了BraodcastReceiver,ContentObserver,File,遊標 
    Cursor,Stream,Bitmap等資源的使用,應該在Activity銷燬時及時關閉或者註銷。

  • 保持對對象生命週期的敏感,特別注意單例、靜態對象、全局性集合等的生命週期。


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