1. 程式人生 > >Android記憶體優化

Android記憶體優化

        避免因不正確使用記憶體 & 缺乏管理,從而出現 記憶體洩露(ML)、記憶體溢位(OOM)、記憶體空間佔用過大 等問題,最終導致應用程式崩潰(Crash

示意圖

下面,將針對回收 程序、物件 、變數的記憶體分配 & 回收進行詳細講解

2、 針對程序的記憶體策略

a. 記憶體分配策略

由 ActivityManagerService 集中管理 所有程序的記憶體分配

b. 記憶體回收策略

  • 步驟1:Application Framework 決定回收的程序型別
    Android中的程序 是託管的;當程序空間緊張時,會 按程序優先順序低->>高的順序 自動回收程序

Android將程序分為5個優先等級,具體如下:

 示意圖

  • 步驟2:Linux 核心真正回收具體程序
    1. ActivityManagerService 對 所有程序進行評分(評分存放在變數adj中)
    2. 更新評分到Linux 核心
    3. Linux 核心完成真正的記憶體回收

此處僅總結流程,這其中的過程複雜,有興趣的讀者可研究系統原始碼ActivityManagerService.java

3、 針對物件、變數的記憶體策略

  • Android的對於物件、變數的記憶體策略同 Java
  • 記憶體管理 = 物件 / 變數的記憶體分配 + 記憶體釋放

下面,將詳細講解記憶體分配 & 記憶體釋放策略

a. 記憶體分配策略

  • 物件 / 變數的記憶體分配 由程式自動 負責
  • 共有3種:靜態分配、棧式分配、 & 堆式分配,分別面向靜態變數、區域性變數 & 物件例項
  • 具體介紹如下

示意圖

注:用1個例項講解 記憶體分配

public class Sample {    
    // 該類的例項物件的成員變數s1、mSample1 & 指向物件存放在堆記憶體中
    int s1 = 0;
    Sample mSample1 = new Sample();   
    
    // 方法中的區域性變數s2、mSample2存放在 棧記憶體
    // 變數mSample2所指向的物件例項存放在 堆記憶體
    public void method() {        
        int s2 = 0;
        Sample mSample2 = new Sample();
    }
}
    // 變數mSample3的引用存放在棧記憶體中
    // 變數mSample3所指向的物件例項存放在堆記憶體
    // 該例項的成員變數s1、mSample1也存放在堆記憶體中
    Sample mSample3 = new Sample();

b. 記憶體釋放策略

  • 物件 / 變數的記憶體釋放 由Java垃圾回收器(GC) / 幀棧 負責
  • 此處主要講解物件分配(即堆式分配)的記憶體釋放策略 = Java垃圾回收器(GC

由於靜態分配不需釋放、棧式分配僅 通過幀棧自動出、入棧,較簡單,故不詳細描述

  • Java垃圾回收器(GC)的記憶體釋放 = 垃圾回收演算法,主要包括:

垃圾收集演算法型別

  • 具體介紹如下

總結

4. 常見的記憶體問題 & 優化方案

  • 常見的記憶體問題如下

    1. 記憶體洩露
    2. 記憶體抖動
    3. 圖片Bitmap相關
    4. 程式碼質量 & 數量
    5. 日常不正確使用
  • 下面,我將詳細分析每項的記憶體問題 & 給出優化方案

4.1 記憶體洩露

  • 簡介
    即 ML (Memory Leak),指 程式在申請記憶體後,當該記憶體不需再使用 但 卻無法被釋放 & 歸還給 程式的現象

  • 對應用程式的影響
    容易使得應用程式發生記憶體溢位,即 OOM

記憶體溢位 簡介:


示意圖
  • 發生記憶體洩露的本質原因

  • 常見記憶體洩露原因

    1. 集合類
    2. Static關鍵字修飾的成員變數
    3. 非靜態內部類 / 匿名類
    4. 資源物件使用後未關閉

本質:物件的引用未被釋放,導致物件本身無法被有效的回收。 
三個知識點:記憶體洩漏、記憶體溢位、記憶體優化工具。

這個時候面試官就被你帶溝裡了,他就會讓你逐個介紹這三個知識點了。 
【記憶體洩漏】 
1.單例模式引發的記憶體洩漏: 
原因:單例模式裡的靜態例項持有物件的引用,導致物件無法被回收,常見為持有Activity的引用 
優化:改為持有Application的引用,或者不持有使用的時候傳遞。

2.集合操作不當引發的記憶體洩漏: 
原因:集合只增不減 
優化:有對應的刪除或解除安裝操作

3.執行緒的操作不當引發的記憶體洩漏: 
原因:執行緒持有物件的引用在後臺執行,與物件的生命週期不一致 
優化:靜態例項+弱引用(Weakrefrence)方式,使其生命週期一致

4.匿名內部類/非靜態內部類操作不當引發的記憶體洩漏: 
原因:內部類持有物件引用,導致無法釋放,比如各種回撥 
優化:保持生命週期一致,改為靜態例項+物件的弱引用方式(WeakReference)

5.常用的資源未關閉回收引發的記憶體洩漏: 
原因:BraodcastReceiver,File,Cursor,IO流,Bitmap等資源使用未關閉 
優化:使用後有對應的關閉和解除安裝機制

6.Handler使用不當造成的記憶體洩漏: 
原因:Handler持有Activity的引用,其傳送的Message中持有Handler的引用,當佇列處理Message的時間過長會導致Handler無法被回收 
優化:靜態例項+弱引用(Weakrefrence)方式 
銷燬物件時候清空佇列裡的Message

記憶體溢位: 
原因: 
1.記憶體洩漏長時間的積累 
2.業務操作使用超大記憶體 
優化: 
1.調整影象大小後再放入記憶體、及時回收 
2.不要過多的建立靜態變數
 

4.2 圖片資源Bitmap相關

  • 優化原因
    即 為什麼要優化圖片Bitmap資源,具體如下圖:

    示意圖
  • 優化方向
    主要 從 以下方面優化圖片Bitmap資源的使用 & 記憶體管理

    示意圖
  • 具體優化方案 下面,我將詳細講解每個優化方向的具體優化方案

示意圖

4.3 記憶體抖動

  • 簡介


    示意圖
  • 優化方案
    儘量避免頻繁建立大量、臨時的小物件

4.4 程式碼質量 & 數量

  • 優化原因 程式碼本身的質量(如 資料結構、資料型別等) & 數量(程式碼量的大小)可能會導致大量的記憶體問題,如佔用記憶體大、記憶體利用率低等

  • 優化方案 主要從程式碼總量、資料結構、資料型別、 & 資料物件引用 方面優化,具體如下


    示意圖

4.5 常見使用

  • 優化原因 一些常見使用也可能引發大量的記憶體問題,下面我將詳細介紹。

  • 優化方案


    示意圖

注:

  1. 還有1個記憶體優化的終極方案:調大 虛擬機器Dalvik的堆記憶體大小
  2. 即 在AndroidManifest.xmlapplication標籤中增加一個android:largeHeap屬性(值 = true),從而通知虛擬機器 應用程式需更大的堆記憶體
  3. 但不建議 & 不鼓勵該做法

4.6 額外小技巧

此處,還有一些記憶體優化的小技巧希望告訴給大家

  • 技巧1:獲取當前可使用的記憶體大小
    呼叫 ActivityManager.getMemoryClass()方法可獲取當前應用可用的記憶體大小(單位 = 兆)

  • 技巧2:獲取當前的記憶體使用情況
    在應用生命週期的任何階段,呼叫 onTrimMemory()獲取應用程式 當前記憶體使用情況(以記憶體級別進行識別),可根據該方法返回的記憶體緊張級別引數 來釋放記憶體

Android 4.0 後提供的一個API

示意圖

  • 技巧3:當檢視變為隱藏狀態時,則釋放記憶體
    當用戶跳轉到不同的應用 & 檢視不再顯示時, 應釋放應用檢視所佔的資源
  1. 注:此時釋放所佔用的資源能顯著的提高系統的快取處理容量
  2. 具體操作:實現當前Activity類的onTrimMemory()後,當用戶離開檢視時會得到通知;若得到返回的引數 = TRIM_MEMORY_UI_HIDDEN 即代表檢視變為隱藏狀態,則可釋放檢視所佔用的資源.

5. 輔助記憶體優化的分析工具

  • 哪怕完全瞭解 記憶體的原因,但難免還是會出現人為難以發現的記憶體問題
  • 下面將簡單介紹幾個主流的輔助分析記憶體優化的工具,分別是
    1. MAT(Memory Analysis Tools)
    2. Heap Viewer
    3. Allocation Tracker
    4. Android Studio 的 Memory Monitor
    5. LeakCanary

5.1 MAT(Memory Analysis Tools)

  • 定義:一個Eclipse的 Java Heap 記憶體分析工具 ->>下載地址
  • 作用:檢視當前記憶體佔用情況

通過分析 Java 程序的記憶體快照 HPROF 分析,快速計算出在記憶體中物件佔用的大小,檢視哪些物件不能被垃圾收集器回收 & 可通過檢視直觀地檢視可能造成這種結果的物件

5.2 Heap Viewer

  • 定義:一個的 Java Heap 記憶體分析工具
  • 作用:檢視當前記憶體快照

可檢視 分別有哪些型別的資料在堆記憶體總 & 各種型別資料的佔比情況

5.3 Allocation Tracker

  • 簡介:一個記憶體追蹤分析工具
  • 作用:追蹤記憶體分配資訊,按順序排列

5.4 Memory Monitor

  • 簡介:一個 Android Studio 自帶 的圖形化檢測記憶體工具

  • 作用:跟蹤系統 / 應用的記憶體使用情況。核心功能如下


    示意圖

5.5 LeakCanary

  • 簡介:一個square出品的Android開源庫 ->>下載地址
  • 作用:檢測記憶體洩露

至此,關於記憶體優化的所有知識講解完畢

6. 總結

  • 本文主要講解記憶體優化的相關知識,總結如下: