Android系統(tǒng)是基于Linux 2.6內(nèi)核開發(fā)的開源操作系統(tǒng),而linux系統(tǒng)的內(nèi)存管理有其獨(dú)特的動態(tài)存儲管理機(jī)制。
不過Android系統(tǒng)對Linux的內(nèi)存管理機(jī)制進(jìn)行了優(yōu)化,Linux系統(tǒng)會在進(jìn)程活動停止后就結(jié)束該進(jìn)程,而Android把這些進(jìn)程都保留在內(nèi)存中,直到系統(tǒng)需要更多內(nèi)存為止。這些保留在內(nèi)存中的進(jìn)程通常情況下不會影響整體系統(tǒng)的運(yùn)行速度,并且當(dāng)用戶再次激活這些進(jìn)程時,提升了進(jìn)程的啟動速度。
Android內(nèi)存管理包含兩部分,一部分是Framework對內(nèi)存的管理,一部分是Linux內(nèi)核對內(nèi)存管理,這兩部分共同決定應(yīng)用程序的生命周期。
在Android中,大部分應(yīng)用程序都運(yùn)行在一個獨(dú)立的Linux進(jìn)程中,每個進(jìn)程都有獨(dú)立的內(nèi)存空間。隨著各種應(yīng)用程序啟動,系統(tǒng)內(nèi)存不斷下降,為了保證新應(yīng)用能夠運(yùn)行,Android需要一套機(jī)制殺死暫時閑置的進(jìn)程。
Android Framework并不能直接回收內(nèi)存,其管理進(jìn)程的服務(wù)(ActivityManagerService,以下簡稱AmS)也同應(yīng)用程序一樣運(yùn)行在Java虛擬機(jī)環(huán)境里。Java虛擬機(jī)都運(yùn)行在各自獨(dú)立的內(nèi)存空間,所以ActivityManagerService沒有辦法感知應(yīng)用程序是否OOM。
Android系統(tǒng)中還運(yùn)行了一個OOM進(jìn)程。該進(jìn)程啟動時首先會在Linux內(nèi)核中把自己注冊為一個OOM Killer。AmS需要把每一個應(yīng)用程序的oom_adj值告知OOM Killer,這個值的范圍在-16到15之間,值越低,說明越重要,這個值類似于Linux中的nice值,只在標(biāo)準(zhǔn)的Linux中,有其自己的OOM Killer。Android中的OOM Killer進(jìn)程僅僅適用于Android應(yīng)用程序。
當(dāng)內(nèi)核的內(nèi)存管理模塊檢測到系統(tǒng)內(nèi)存不足時就會通知OOM Killer,然后OOM Killer根據(jù)AmS所告知的優(yōu)先級強(qiáng)制退出優(yōu)先級低的應(yīng)用程序。
Android 內(nèi)存管理機(jī)制
基于Linux內(nèi)核OOM Killer的核心思想,Android 系統(tǒng)擴(kuò)展出了自己的內(nèi)存監(jiān)控體系。因?yàn)長inux下的內(nèi)存殺手需要等到系統(tǒng)資源”瀕臨絕境”的情況下才會產(chǎn)生效果。
而Android則實(shí)現(xiàn)了自己的Killer。Android 系統(tǒng)為此開發(fā)了一個專門的驅(qū)動,名為Low Memory Killer(LMK)。源碼路徑在內(nèi)核工程的drivers/staging/android/Lowmemorykiller.c中。
它的驅(qū)動加載函數(shù)如下:
static int __init lowmem_init(void)
{
register_shrinker(&lowmem_shrinker);
return 0;
}
當(dāng)系統(tǒng)的空閑頁面低于一定閾值時,這個回調(diào)就會被執(zhí)行。
Lowmemorykiller.c 中定義了兩個數(shù)組,分別如下:
static short lowmem_adj[6] = {
0,
1,
6,
12,
};
static int lowmem_adj_size = 4;//下面的數(shù)值以此為單位(頁大小)
static int lowmem_minfree[6] = {
3 * 512, /* 6MB */
2 * 1024, /* 8MB */
4 * 1024, /* 16MB */
16 * 1024, /* 64MB */
};
第一個數(shù)組lowmem_adj最多有6個元素,默認(rèn)只定義了4個,它表示可用容量處于”某層級”時需要被處理的adj值;
第二個數(shù)組則是對”層級”的描述。舉個例子,lowmem_minfree 的第一個元素是
3*512*lowmem_adj_size=6MB,意味著當(dāng)可用內(nèi)存小于6MB時,Killer需要清理adj的值為0(即lowmem_adj的第一個元素)以下的那些進(jìn)程。其中adj的取值范圍是-17~15,數(shù)字越小表示進(jìn)程級別越高,通常只有0-15被使用。

android進(jìn)程優(yōu)先級
android將進(jìn)程的優(yōu)先級分為5個層次,按照優(yōu)先級由高到低排列如下:
- 前臺進(jìn)程(Foreground process):它表明用戶正在與該進(jìn)程進(jìn)行交互操作,android系統(tǒng)依據(jù)下面的條件來將一個進(jìn)程標(biāo)記為前臺進(jìn)程:
- 該進(jìn)程持有一個用戶正在與其交互的Activity(也就是這個activity的生命周期方法走到了onResume()方法)。
- 該進(jìn)程持有一個Service,并且這個Service與一個用戶正在交互中的Activity進(jìn)行綁定。
- 該進(jìn)程持有一個前臺運(yùn)行模式的Service(也就是這個Service調(diào)用了startForegroud()方法)。
- 該進(jìn)程持有一個正在執(zhí)行生命周期方法(onCreate()、onStart()、onDestroy()等)的Service。
- 該進(jìn)程持有一個正在執(zhí)行onReceive()方法的BroadcastReceiver。
一般情況下,不會有太多的前臺進(jìn)程。殺死前臺進(jìn)程是操作系統(tǒng)最后無可奈何的做法。當(dāng)內(nèi)存嚴(yán)重不足的時候,前臺進(jìn)程一樣會被殺死。
- 可見進(jìn)程(Visible process):它表明雖然該進(jìn)程沒有持有任何前臺組件,但是它還是能夠影響到用戶看得到的界面。android系統(tǒng)依據(jù)下面的條件將一個進(jìn)程標(biāo)記為可見進(jìn)程:
- 該進(jìn)程持有一個非前臺Activity,但這個Activity依然能被用戶看到(也就是這個Activity調(diào)用了onPause()方法)。例如,當(dāng)一個activity啟動了一個對話框,這個activity就被對話框擋在后面。
- 該進(jìn)程持有一個與可見(或者前臺)Activity綁定的Service。
服務(wù)進(jìn)程(Service process):除了符合前臺進(jìn)程和可見進(jìn)程條件的Service,其它的Service都會被歸類為服務(wù)進(jìn)程。
后臺進(jìn)程(Background process):持有不可見Activity(調(diào)用了onStop()方法)的進(jìn)程即為后臺進(jìn)程。通常情況下都會有很多后臺進(jìn)程,當(dāng)內(nèi)存不足的時候,在所有的后臺進(jìn)程里面,會按照LRU(最近使用)規(guī)則,優(yōu)先回收最長時間沒有使用過的進(jìn)程。
空進(jìn)程(Empty process):不持有任何活動組件的進(jìn)程。保持這種進(jìn)程只有一個目的,就是為了緩存,以便下一次啟動該進(jìn)程中的組件時能夠更快響應(yīng)。當(dāng)資源緊張的時候,系統(tǒng)會平衡進(jìn)程緩存和底層的內(nèi)核緩存情況進(jìn)行回收。
前臺>可見>服務(wù)>后臺>空
如果一個進(jìn)程同時滿足上述5種優(yōu)先級中的多個等級條件,android系統(tǒng)會優(yōu)先選取其中最高的等級作為該進(jìn)程的優(yōu)先級。
內(nèi)存管理機(jī)制的特點(diǎn)
- 更少的占用內(nèi)存;
- 在合理的時候,合理的釋放內(nèi)存;
- 在系統(tǒng)內(nèi)存緊張的情況下,能釋放大部分不重要的的資源,來為Android提供可用的資源;
- 能夠很合理的在特殊生命周期中,保存和回復(fù)還原重要數(shù)據(jù),以至于系統(tǒng)能夠正確的重新恢復(fù)該應(yīng)用。
一些內(nèi)存優(yōu)化的方法
- 當(dāng)Service完成任務(wù)后,盡量停止它,或者用intentService代替;
- 在UI不可見的時候,釋放掉一些只有UI使用的資源;
- 在系統(tǒng)資源內(nèi)存緊張的時候,盡可能多的釋放掉一些非重要資源;
- 避免濫用Bitmap導(dǎo)致的內(nèi)存浪費(fèi),參見Here
- 使用針對內(nèi)存優(yōu)化過的數(shù)據(jù)容器
- 避免使用依賴注入的框架
- 使用ZIP對其APK
- 使用多進(jìn)程