Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 內存管理機制詳解

Android 內存管理機制詳解

編輯:關於Android編程

??嵌入式設備的一個普遍特點是內存容量相對有限。當運行的程序超過一定數量時,或者涉及復雜的計算時,很可能出現內存不足,進而導致系統卡頓的現象。Android 系統也不例外,它同樣面臨著設備物理內存短缺的困境。對於已經啟動過一次的Android程序,再一次啟動所花的時間會明顯減少。原因在於Android系統並不馬上清理那些已經”淡出視野”的程序(比如你調用Activity.finish退出UI界面)。它們在一定的時間裡仍然駐留在內存中。這樣做的好處是明顯的,即下一次啟動不需要再為程序重新創建一個進程;壞處就是,加大了內存OOM的概率。

Linux內存監控機制(OOMKiller)

??Android是基於Linux的,而Linux底層內核有自己的內存監控機制,即OOMKiller。一旦發現系統的可用內存達到臨界值,這個OOM的管理者就會自動跳出來清理內存。

OOMKiller有不同的策略和不同的處理手段。它的核心思想如下:

按照優先級順序,從低到高逐步殺掉進程,回收內存。

優先級的設定策略主要考慮兩個方面:一個是要考慮對系統的損害程度(例如系統的核心進程,優先級通常較高),另一方面也希望盡可能多地釋放無用內存。一個合理的策略至少需要考慮以下幾個因素:

進程消耗的內存 進程占用的CPU時間 oom_adj(OOM權重)

??內核所管理的進程都有一個衡量其oom權重的值,存儲在/proc/< PID >/oom_adj中。根據這一權重值以及上面所提及的若干其他因素,系統會實時給每個進程評分,以決定OOM時應該殺死哪些進程。
這個值存儲在/proc/< PID >/oom_score中。
oom_score分數越低的進程,被殺死的概率越小,或者說被殺死的時間越晚。
下面展示了PID為5912的NetworkManager進程的oom_adj 和oom_score,可以看到分數很低,說明此進程十分重要,一般不會被系統殺死。

oom_score

Android 內存管理機制

基於Linux內核OOM Killer的核心思想,Android 系統擴展出了自己的內存監控體系。因為Linux下的內存殺手需要等到系統資源”瀕臨絕境”的情況下才會產生效果,而Android則實現了自己的Killer.

Android 系統為此開發了一個專門的驅動,名為Low Memory Killer(LMK)。源碼路徑在內核工程的 drivers/staging/android/Lowmemorykiller.c中。
它的驅動加載函數如下:

static int __init lowmem_init(void)
{
    register_shrinker(&lowmem_shrinker);
    return 0;
}

可見LMK向內核線程注冊了一個shrinker的監聽回調,實現體為lowmem_shrinker。當系統的空閒頁面低於一定阈值時,這個回調就會被執行。

Lowmemorykiller.c 中定義了兩個數組,分別如下:

static short lowmem_adj[6] = {
    0,
    1,
    6,
   12,
 };
static int lowmem_adj_size = 4;//下面的數值以此為單位(頁大小)
static int lowmem_minfree[6] = {
    3 * 512,    /* 6MB */
    2 * 1024,   /* 8MB */
    4 * 1024,   /* 16MB */
    16 * 1024,  /* 64MB */
};

第一個數組lowmem_adj最多有6個元素,默認只定義了4個,它表示可用容量處於”某層級”時需要被處理的adj值;第二個數組則是對”層級”的描述。這樣說可能不清楚,舉個例子,lowmem_minfree 的第一個元素是3*512,3*512*lowmem_adj_size=6MB.意味著當可用內存小於6MB時,Killer需要清理adj的值為0(即lowmem_adj的第一個元素)以下的那些進程。其中adj的取值范圍是-17~15,數字越小表示進程級別越高,通常只有0-15被使用。

下圖是LWK機制的實現簡圖。

LWK實現簡圖

這兩個數組只是系統的預定義值,我們可以根據項目的實際需求來定制。

Android系統提供了相應的文件來供我們修改這兩組值。

路徑如下:

/sys/module/lowmemorykiller/parameters/adj
/sys/module/lowmemorykiller/parameters/minfree

可以在 init.rc(系統啟動時,由init進程解析的第一個腳本)中加入如下語句。init.rc路徑為/system/core/rootdir/init.rc

/sys/module/lowmemorykiller/parameters/adj  0,8
/sys/module/lowmemorykiller/parameters/minfree 1024,4096

Android進程分類

進程omm_adj的大小跟進程的類型以及進程被調度的次序有關。

在Android中,進程主要分為以下幾個種類:

1. 前台進程(foreground)

??目前正在屏幕上顯示的進程和一些系統進程。舉例來說,Dialer,Storage,Google Search等系統進程就是前台進程;再舉例來說,當你運行一個程序,如浏覽器,當浏覽器界面在前台顯示時,浏覽器屬於前台進程(foreground),但一旦你按home回到主界面,浏覽器就變成了後台程序(background)。我們最不希望終止的進程就是前台進程。

2. 可見進程(visible)

??可見進程是一些不再前台,但用戶依然可見的進程,舉個例來說:widget、輸入法等,都屬於visible。這部分進程雖然不在前台,但與我們的使用也密切相關,我們也不希望它們被終止(你肯定不希望時鐘、天氣,新聞等widget被終止,那它們將無法同步,你也不希望輸入法被終止,否則你每次輸入時都需要重新啟動輸入法)。

3. 桌面進程(home app)

??即launcher,保證在多任務切換之後,可以快速返回到home界面而不需重新加載launcher。

4. 次要服務(secondary server)

??目前正在運行的一些服務(主要服務,如撥號等,是不可能被進程管理終止的,故這裡只談次要服務),舉例來說:谷歌企業套件,Gmail內部存儲,聯系人內部存儲等。這部分服務雖然屬於次要服務,但很一些系統功能依然息息相關,我們時常需要用到它們,所以也不太希望他們被終止。

5. 後台進程(hidden)

??即是後台進程(background),就是我們通常意義上理解的啟動後被切換到後台的進程,如浏覽器,閱讀器等。當程序顯示在屏幕上時,他所運行的進程即為前台進程(foreground),一旦我們按home返回主界面(注意是按home,不是按back),程序就駐留在後台,成為後台進程(background)。後台進程的管理策略有多種:有較為積極的方式,一旦程序到達後台立即終止,這種方式會提高程序的運行速度,但無法加速程序的再次啟動;也有較消極的方式,盡可能多的保留後台程序,雖然可能會影響到單個程序的運行速度,但在再次啟動已啟動的程序時,速度會有所提升。這裡就需要用戶根據自己的使用習慣找到一個平衡點。

6. 內容供應節點(content provider)

沒有程序實體,進提供內容供別的程序去用的,比如日歷供應節點,郵件供應節點等。在終止進程時,這類程序應該有較高的優先權。

7. 空進程(empty)

??沒有任何東西在內運行的進程,有些程序,比如BTE,在程序退出後,依然會在進程中駐留一個空進程,這個進程裡沒有任何數據在運行,作用往往是提高該程序下次的啟動速度或者記錄程序的一些歷史信息。這部分進程無疑是應該最先終止的

在AMS 用於處理進程的相關代碼文件ProcessList.java 中,定義了不同類型的adj值,如下所示:

/**
*省略其它代碼
*/
    //未知的adj
    static final int UNKNOWN_ADJ = 16;
    static final int CACHED_APP_MAX_ADJ = 15;
    static final int CACHED_APP_MIN_ADJ = 9;
    //B list of service ,和A list相比,對用戶黏合度小一些
    static final int SERVICE_B_ADJ = 8;
    //用戶前一次交互的進程
    static final int PREVIOUS_APP_ADJ = 7;
    //Launcher進程
    static final int HOME_APP_ADJ = 6;
    //運行了application service的進程
    static final int SERVICE_ADJ = 5;
    //重量級應用程序進程
    static final int HEAVY_WEIGHT_APP_ADJ = 4;
    //用於承載backup相關操作的進程
    static final int BACKUP_APP_ADJ = 3;
    //這類進程能被用戶感覺到但不可見,如後台運行的音樂播放器
    static final int PERCEPTIBLE_APP_ADJ = 2;
    //有前台可見的Activity進程,如果輕易殺死這類進程,將嚴重影響用戶體驗
    static final int VISIBLE_APP_ADJ = 1;
    //當前正在運行的那個進程,即用戶正在交互的程序
    static final int FOREGROUND_APP_ADJ = 0;
    static final int PERSISTENT_SERVICE_ADJ = -11;
    //persistent性質的進程,如telephony
    static final int PERSISTENT_PROC_ADJ = -12;
    //系統進程
    static final int SYSTEM_ADJ = -16;

/**
*省略其它代碼
*/

上面定義的adj數值來看,adj越小表示進程類型就越重要,特別的,系統進程的默認oom_adj 為-16,這類進程永遠不會被殺死。

還需要注意的是updateOomLevels函數,內部原理是通過寫上面兩個文件來實現,AMS 會根據系統的當前配置自動修正adj和minfree,以盡可能適配不同的硬件。函數源碼如下所示:

<code><code><code>private void updateOomLevels(int displayWidth, int displayHeight, boolean write) {
        // Scale buckets from avail memory: at 300MB we use the lowest values to
        // 700MB or more for the top values.
        float scaleMem = ((float)(mTotalMemMb-300))/(700-300);

        // Scale buckets from screen size.
        int minSize = 480*800;  //  384000
        int maxSize = 1280*800; // 1024000  230400 870400  .264
        float scaleDisp = ((float)(displayWidth*displayHeight)-minSize)/(maxSize-minSize);
        if (false) {
            Slog.i("XXXXXX", "scaleMem=" + scaleMem);
            Slog.i("XXXXXX", "scaleDisp=" + scaleDisp + " dw=" + displayWidth
                    + " dh=" + displayHeight);
        }

        float scale = scaleMem > scaleDisp ? scaleMem : scaleDisp;
        if (scale < 0) scale = 0;
        else if (scale > 1) scale = 1;
        int minfree_adj = Resources.getSystem().getInteger(
                com.android.internal.R.integer.config_lowMemoryKillerMinFreeKbytesAdjust);
        int minfree_abs = Resources.getSystem().getInteger(
                com.android.internal.R.integer.config_lowMemoryKillerMinFreeKbytesAbsolute);
        if (false) {
            Slog.i("XXXXXX", "minfree_adj=" + minfree_adj + " minfree_abs=" + minfree_abs);
        }

        // We've now baked in the increase to the basic oom values above, since
        // they seem to be useful more generally for devices that are tight on
        // memory than just for 64 bit.  This should probably have some more
        // tuning done, so not deleting it quite yet...
        final boolean is64bit = false; //Build.SUPPORTED_64_BIT_ABIS.length > 0;

        for (int i=0; i<moomadj.length; 64="" int="" low="mOomMinFreeLow[i];" high="mOomMinFreeHigh[i];" if="" bit="" we="" consume="" more="" baseline="" because="" is="" to="" avoid="" being="" all="" pagey="" and="" scale="" up="" the="" memory="" levels="" give="" us="" some="" breathing="" room.="" minfree_abs="" data-cke-pa-on="">= 0) {
            for (int i=0; i<moomadj.length; 1="" 3="" 4="" moomadj.length="" -="" if="" minfree_adj="" for="" int="" i="0;" the="" maximum="" size="" we="" will="" restore="" a="" process="" from="" cached="" to="" when="" under="" memory="" is="" have="" reserved="" kernel="" caches="" and="" other="" overhead="" before="" killing="" background="" processes.="" mcachedrestorelevel="(getMemLevel(ProcessList.CACHED_APP_MAX_ADJ)/1024)" ask="" try="" keep="" enough="" free="" allocate="" full="" screen="" 32bpp="" buffers="" without="" entering="" direct="" reclaim.="" reserve="displayWidth" displayheight="" reserve_adj="Resources.getSystem().getInteger(com.android.internal.R.integer.config_extraFreeKbytesAdjust);" reserve_abs="">= 0) {
            reserve = reserve_abs;
        }

        if (reserve_adj != 0) {
            reserve += reserve_adj;
            if (reserve < 0) {
                reserve = 0;
            }
        }

        if (write) {
            ByteBuffer buf = ByteBuffer.allocate(4 * (2*mOomAdj.length + 1));
            buf.putInt(LMK_TARGET);
            for (int i=0; i<moomadj.length; 8192="" 20480="" gb:="" hc:="" pre="">

改變進程權重adj值

除了系統的評定標准,我們也可以用自己的方式來改變進程的權重值。

1.寫文件

??和上述提到的adj和minfree類似,進程的oom_adj也可以通過寫文件的形式來修改,路徑為/proc/< PID >/oom_adj。比如init.rc中有如下語句

on early-init
 # Set init and its forked children's oom_adj.
 write /proc/1/oom_adj -16

PID 為1的進程為init程序,將此進程的oom_adj 設置為-16,保證它不會被殺死。

2.設置android:persistent

??對某些非常重要的程序,不希望它被系統殺死。在AndroidMainifest.xml文件中給application 標簽添加”android:persistent=true”屬性,將應用程序設置為常駐內存,但需要特別注意,如果應用程序本不夠完善,而系統又不能正常回收,那麼會導致意想不到的問題。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved