你瞭解Android LMK機制麼?

前言

上文主要介紹了Andorid內存的管理機制,本文對其中的LMK機制進行深入擴展總結。

我們知道出現Crash應用閃退和崩潰一般有三個原因:ANR(程序無響應)、Exception(異常)、LMK(低內存殺死機制)。本文重點介紹LMK機制。

目的:瞭解LMK原理,探究進程保活的方案和程序異常處理的方法。

一、原理篇

1. 什麼是Android LMK

KMK,(Low Memory Killer )低內存殺死機制。由於Android應用的沙箱機制,每個應用程序都運行在一個獨立的進程中,各自擁有獨立的Dalvik虛擬機實例,系統默認分配給虛擬機的內存是有限度的,當系統內存太低依然會觸發LMK機制,即出現閃退、崩潰現象。

不同廠商不同,如:華爲mate7,192M ;小米4,128M ;紅米,128M 。而在,Android4.0以後,可以通過在application節點中設置屬性android:largeHeap=”true”來設置最大可分配多少內存空間就可以突破一定限制。

2. OOM

OOM(OutOfMemoryError)內存溢出錯誤 。導致OOM的兩個主要原因:

1、內存泄漏,大量無用對象未及時回收,導致後續申請內存失敗。

2、BitMap大對象,幾個大圖同時加載很容易觸發OOM。

通過閱讀本文,可以瞭解Android LMK機制,從而避免App被頻繁的殺死,以及一些開發中會遇到的問題。

爲了空出足夠的內存供前臺進程使用,Android會定時進行CHECK進程樹,然後殺死優先級別不高的進程。而進程的優先級別是按照屬性 oom_adj 來判斷的。oom_adj數值越低,越不會被殺死。

3. oom_adj 的值是如何賦予的

oom_adj的數字大致爲這幾種:

FOREGROUND_APP_ADJ 0 前臺進程,正在活動的Activity或者使用startForeground的Service
VISIBLE_APP_ADJ 1 可見進程,不可操作的Activity,但是可見
SECONDARY_SERVER_ADJ 2 擁有後臺服務器的進程
HIDDEN_APP_MIN_ADJ 7 Activity沒有完全退出,直接採用 moveTaskToBack 到HOME的進程
CONTENT_PROVIDER_ADJ 14 內容提供進程
EMPTY_APP_ADJ 15 空程序,既不提供服務,也不提供內容
CORE_SERVER_ADJ -12 系統進程
SYSTEM_ADJ -16 系統核心服務(進程永遠不會被殺掉)

當系統的內存不足的時候,那麼就會殺死發送KILL SIGNAL, 殺死一些優先級別低的進程,用來提供足夠的內存給前臺進程使用。

用命令可以查看進程的優先級的值:

adb shell dumpsys activity|grep oom_adj 

在這裏插入圖片描述

查看進程命令:

adb shell dumpsys activity processes

在這裏插入圖片描述

注意:

  • Low memory killer 是定時進行檢查。

  • Low memory killer 主要是通過進程的oom_adj 來判定進程的重要程度。這個值越小,程序越重要,被殺的可能性越低。

  • oom_adj的大小和進程的類型以及進程被調度的次序有關。

4. LMK的工作機制

LMK開始工作時,首先根據閾值表確定當前的警戒級數,則高於警戒級數的進程是待殺的範圍。

然後遍歷所有進程的oom_adj值,找到大於min_adj的進程,若找到多個,則把佔用進程最大的進程存放在selected中。最關鍵的一步就是,發送SIGKILL信息,殺掉該進程。

5.Android進程優先級

5.1 Android進程的優先級

一般情況下,Android會盡可能的保持應用進程,但在特定的場景會對進程進行Kill,例如爲了清除舊進程來回收內存等。爲了區分哪些進程最先被回收清理,而哪些不會,有一個優先級別,這就是Android的進程優先級,具體包括以下5種(優先級從高到低)。

  • Foreground/Activate process 前臺進程。用戶當前操作的進程,包括用戶正在交互的Activity,綁定用戶正在交互Activity的Service,使用startForeground的Service,正在執行onReceive的BroadcastReceiver等。
  • Visible process 可見進程。會影響用戶所見內容的進程,如onPause狀態的Activity等。
  • Service process 服務進程。後臺服務,如正在運行startService啓動的Service。
  • Background process 後臺進程。對用戶交互無影響,如onStop狀態的Activity等。
  • Empty process 空進程。一般用作緩存以縮短下次啓動時間,系統往往會終止這些空進程。

5.2. Android進程的回收策略

Android主要通過LMK(Low Memory Killer)來對進程進行回收管理,LMK是在Android系統內存不足而選擇kill部分進程釋放空間,生死大權的決定者,其基於Linux的OOM機制,LMK通過oom_adj與佔用內存的大小決定要殺死的進程,oom_adj值越小,越不容易被殺死。上面的幾種進程形態對於的不同的oom_adj值。前臺進程的優先級爲0,普通service的進程優先級是8。

5.3 保活的方法

一方面提高進程優先級,降低被系統kill的概率。另一方面,在App被殺死以後進行拉活。知道了原理我們就可以採用一定的策略來提高應用的存活機率。

二、方法篇

進程保活說白了就是保證自己App進程不死,App被殺死有以下幾種可能:

  • 被系統殺死
  • 被用戶殺死
  • 被競爭對手殺死

瞭解LMK機制後,就可以對系統殺死的情況做相對應的優化。

  1. 通過在androidmanifest.xml中的application標籤中加入android:persistent="true"屬性後的確就能夠達到保證該應用程序所在進程不會被LMK殺死。

但有個前提就是應用程序必須是系統應用,也就是說應用程序不能採用通常的安裝方式。必須將應用程序的apk包直接放到/system/app目錄下。而且必須重啓系統後才能生效。

應用場景:定製TV端、平板端和手機端預裝系統軟件。

關鍵詞:加入白名單、成爲系統軟件。

有些手機廠商把這些知名的app放入了自己的白名單中,保證了進程不死來提高用戶體驗(如微信、QQ、百度全家桶等在小米的白名單中)。如果從白名單中移除,他們終究還是和普通app一樣躲避不了被殺的命運。

  1. 在程序中考慮異常處理,如恢復數據狀態
static final String STATE_SCORE = "playerScore";
static final String STATE_LEVEL = "playerLevel";
...

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    // 保存用戶自定義的狀態
    savedInstanceState.putInt(STATE_SCORE, mCurrentScore);
    savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel);
    
    // 調用父類交給系統處理,這樣系統能保存視圖層次結構狀態
    super.onSaveInstanceState(savedInstanceState);
}

說明:Activity 被系統回收了,在回收之前保存當前狀態。
重寫onSaveInstanceState()方法,在此方法中保存需要保存的數據,該方法會在Activity被回收前調用。
通過重寫onRetoreInstanceState()方法可以從中提取保存好的數據。

  1. 程序退出時 做類似Home處理。將程序退到後臺而不是kill程序。

在根Activity中重寫後退按鈕響應事件,當按後退按鈕的時候把Activity退置到後臺

     @Override
     public boolean onKeyUp(int keyCode, KeyEvent event) {
         if (keyCode == KeyEvent.KEYCODE_BACK) {
                moveTaskToBack(true);        
             return true;
         }
         return super.onKeyUp(keyCode, event);
     }

其中的moveTaskToBack

   /**
     * Move the task containing this activity to the back of the activity
     * stack.  The activity's order within the task is unchanged.
     *
     * @param nonRoot If false then this only works if the activity is the root
     *                of a task; if true it will work for any activity in
     *                a task.
     *
     * @return If the task was moved (or it was already at the
     *         back) true is returned, else false.
     */ 
public boolean moveTaskToBack(boolean nonRoot) {
        try {
            return ActivityManager.getService().moveActivityTaskToBack(
                    mToken, nonRoot);
        } catch (RemoteException e) {
            // Empty
        }
        return false;
    }

說明:當nonRoot 爲 false 時,當前activity必須爲棧底,也就是最底層的activity,如果其他activity沒有及時finish掉,就會出現異常,導致崩潰等情況的發生;nonRoot 爲 true 時,不需要考慮當前activity是否在棧底。

  1. 程序性能優化,迴歸問題的本質。

系統統內存不足的時候肯定優先殺死這些佔用內存高的進程來騰出資源。所以,爲了儘量避免後臺UI進程被殺,需要儘可能的釋放一些不用的資源,尤其是圖片、音視頻之類的。對此,我們要想程序不被殺死,必須當前程序的內存佔空間有比其他程序小纔有競爭力不被優化kill掉。

最後在進一步總結LMK的機制和實際運用的意義時,看看這個博主是如何總結的:

關於瞭解LMK之後的內存管理建議:

  1. 我個人是長期在手機廠商從事APP和framework開發的。從外部來看,如果一個應用的內存總是不夠用而又經常被回收,那麼是可以從各個層面來緩解這種情況的——設置應用進程的oom_adj爲更低(這種修改不僅可在底層也可在app層進行),修改各等級閥值等等,這是在不涉及應用本身修改的情況下,能做出的有限修改。可惜對於日常的應用開發者來說,這絕對不是康莊大道。
  2. 對於一般的應用開發者而言,安卓設備的底層已經封閉,甚至ROOT權限也無法獲得,他們能做的更多是從應用自身進行優化,來使得應用更不容易被殺死回收。要做到這一點,就要深刻理解LMK的權重系統。比如運行那些需要後臺運行的任務,用service而不要用後臺進程;又比如如果你在開發輸入法等應用,你自然能通過某些設置使得它變成高優先級的可視進程而不是後臺進程。
  3. 當然,既然有所謂閥值,最治本的方法當然是減少進程的內存佔用。作爲普通應用開發者,你不能默認自己的應用能始終在前臺,更不可能無限制地重啓自己的進程(最好別這麼幹),提高應用各層級的閥值,鍥而不捨地減少應用各個部分所佔用的內存,並更順暢地幫助內存的釋放(是的。。。JAVA),纔是開發內存佔用小,運行穩定順暢的好應用的優秀開發者素質。

三、總結

瞭解Android LMK機制後,有助於在開發程序時 對系統的內存回收機制採取一定的措施來提高程序的體驗性,同時最核心的問題還是在程序的開發內功上做指導意義,畢竟,如何管理好內存,是我們一直需要探討和實踐的問題。未完待續……

參考資料:

1.onSaveInstanceState()和onRestoreInstanceState()使用詳解

2.Android之進程回收機制LMK(Low Memory Killer)

3.Android APP開發的內存管理與優化之一 ——LowMemory Killer

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