作者: 強(qiáng)波 (阿里云OS平臺(tái)部-Cloud Engine)
博客: http://qiangbo.space/
本文是Android系統(tǒng)進(jìn)程管理的第三篇文章。進(jìn)程管理的前面兩篇文章,請(qǐng)參見這里:
本文適合Android平臺(tái)的應(yīng)用程序開發(fā)者,也適合對(duì)于Android系統(tǒng)內(nèi)部實(shí)現(xiàn)感興趣的讀者。
前言
內(nèi)存是系統(tǒng)中非常寶貴的資源,即便如今的移動(dòng)設(shè)備上,內(nèi)存已經(jīng)達(dá)到4G甚至6G的級(jí)別,但對(duì)于內(nèi)存的回收也依然重要,因?yàn)樵贏ndroid系統(tǒng)上,同時(shí)運(yùn)行的進(jìn)程有可能會(huì)有幾十甚至上百個(gè)之多。
如何將系統(tǒng)內(nèi)存合理的分配給每個(gè)進(jìn)程,以及如何進(jìn)行內(nèi)存回收,便是操作系統(tǒng)需要處理的問題之一。
本文會(huì)講解Android系統(tǒng)中內(nèi)存回收相關(guān)的知識(shí)。
對(duì)于內(nèi)存回收,主要可以分為兩個(gè)層次:
- 進(jìn)程內(nèi)的內(nèi)存回收:通過釋放進(jìn)程中的資源進(jìn)行內(nèi)存回收
- 進(jìn)程級(jí)的內(nèi)存回收:通過殺死進(jìn)程來進(jìn)行內(nèi)存回收
這其中,進(jìn)程內(nèi)的內(nèi)存回收主要分為兩個(gè)方面:
- 虛擬機(jī)自身的垃圾回收機(jī)制
- 在系統(tǒng)內(nèi)存狀態(tài)發(fā)生變化時(shí),通知應(yīng)用程序,讓開發(fā)者進(jìn)行內(nèi)存回收
而進(jìn)程級(jí)的內(nèi)存回收主要是依靠系統(tǒng)中的兩個(gè)模塊,它們是:
- Linux OOM Killer
- LowMemoryKiller
在特定場(chǎng)景下,他們都會(huì)通過殺死進(jìn)程來進(jìn)行內(nèi)存回收。
Android系統(tǒng)的內(nèi)存管理簡(jiǎn)介
在Android系統(tǒng)中,進(jìn)程可以大致分為系統(tǒng)進(jìn)程和應(yīng)用進(jìn)程兩大類。
系統(tǒng)進(jìn)程是系統(tǒng)內(nèi)置的(例如:init
,zygote
,system_server
進(jìn)程),屬于操作系統(tǒng)必不可少的一部分。系統(tǒng)進(jìn)程的作用在于:
- 管理硬件設(shè)備
- 提供訪問設(shè)備的基本能力
- 管理應(yīng)用進(jìn)程
應(yīng)用進(jìn)程是指應(yīng)用程序運(yùn)行的進(jìn)程。這些應(yīng)用程序可能是系統(tǒng)出廠自帶的(例如Launcher,電話,短信等應(yīng)用),也可能是用戶自己安裝的(例如:微信,支付寶等)。
Android中應(yīng)用進(jìn)程通常都運(yùn)行在Java虛擬機(jī)中。在Android 5.0之前的版本,這個(gè)虛擬機(jī)是Dalvik,5.0及之后版本,Android引入了新的虛擬機(jī),稱作Android Runtime,簡(jiǎn)稱“ART”。
關(guān)于ART和Dalvik可以參見這里:ART and Dalvik。無論是Dalvik還是ART,本身都具有垃圾回收的能力,關(guān)于這一點(diǎn),我們?cè)诤竺鎸iT講解。
Android的應(yīng)用程序都會(huì)依賴一些公共的資源,例如:Android SDK提供的類和接口,以及Framework公開的圖片,字符串等。為了達(dá)到節(jié)省內(nèi)存的目的,這些資源在內(nèi)存中并不是每個(gè)應(yīng)用進(jìn)程單獨(dú)一份拷貝。而是會(huì)在所有應(yīng)用之間共享,因?yàn)樗袘?yīng)用進(jìn)程都是作為Zygote進(jìn)程fork出來的子進(jìn)程。關(guān)于這部分內(nèi)容,我們已經(jīng)在Android系統(tǒng)中的進(jìn)程管理:進(jìn)程的創(chuàng)建一文中講解過。
在Java語言中,通過new
創(chuàng)建的對(duì)象都會(huì)在堆中分配內(nèi)存。應(yīng)用程序堆的大小是有限的。系統(tǒng)會(huì)根據(jù)設(shè)備的物理內(nèi)存大小來確定每個(gè)應(yīng)用程序所允許使用的內(nèi)存大小,一旦應(yīng)用程序使用的內(nèi)存超過這個(gè)大小,便會(huì)發(fā)生OutOfMemoryError。
因此開發(fā)者需要關(guān)心應(yīng)用的內(nèi)存使用狀況。關(guān)于如何監(jiān)測(cè)應(yīng)用程序的內(nèi)存使用,可以參見這里:Investigating Your RAM Usage。
開發(fā)者相關(guān)的API
下面是一些與內(nèi)存相關(guān)的開發(fā)者API,它們是Android SDK的一部分。
ComponentCallbacks2
Android系統(tǒng)會(huì)根據(jù)當(dāng)前的系統(tǒng)內(nèi)存狀態(tài)和應(yīng)用的自身狀態(tài)對(duì)應(yīng)用進(jìn)行通知。這種通知的目的是希望應(yīng)用能夠感知到系統(tǒng)和自身的狀態(tài)變化,以便開發(fā)者可以更準(zhǔn)確的把握應(yīng)用的運(yùn)行。
例如:在系統(tǒng)內(nèi)存充足時(shí),為了提升響應(yīng)性能,應(yīng)用可以緩存更多的資源。但是當(dāng)系統(tǒng)內(nèi)存緊張時(shí),開發(fā)者應(yīng)當(dāng)釋放一定的資源來緩解內(nèi)存緊張的狀態(tài)。
ComponentCallbacks2
接口中的void onTrimMemory(int level)
回調(diào)函數(shù)用來接收這個(gè)通知。關(guān)于這一點(diǎn),在“開發(fā)者的內(nèi)存回收”一節(jié),我們會(huì)詳細(xì)講解。
ActivityManager
ActivityManager,從名稱中就可以看出,這個(gè)類是用來管理Activity的系統(tǒng)服務(wù)。但這個(gè)類中也包含了很多運(yùn)行時(shí)狀態(tài)查詢的接口,這其中就包括與內(nèi)存相關(guān)的幾個(gè):
-
int getMemoryClass ()
獲取當(dāng)前設(shè)備上,單個(gè)應(yīng)用的內(nèi)存大小限制,單位是M。注意,這個(gè)函數(shù)的返回值只是一個(gè)大致的值。 -
void getMemoryInfo (ActivityManager.MemoryInfo outInfo)
獲取系統(tǒng)的內(nèi)存信息,具體結(jié)構(gòu)可以查看ActivityManager.MemoryInfo,開發(fā)者最關(guān)心的可能就是availMem
以及totalMem
。 -
void getMyMemoryState (ActivityManager.RunningAppProcessInfo outState)
獲取調(diào)用進(jìn)程的內(nèi)存信息 -
MemoryInfo[] getProcessMemoryInfo (int[] pids)
通過pid獲取指定進(jìn)程的內(nèi)存信息 -
boolean isLowRamDevice()
查詢當(dāng)前設(shè)備是否是低內(nèi)存設(shè)備
Runtime
Java應(yīng)用程序都會(huì)有一個(gè)Runtime接口的實(shí)例,通過這個(gè)實(shí)例可以查詢運(yùn)行時(shí)的一些狀態(tài),與內(nèi)存相關(guān)的接口有:
-
freeMemory()
獲取Java虛擬機(jī)的剩余內(nèi)存 -
maxMemory()
獲取Java虛擬機(jī)所能使用的最大內(nèi)存 -
totalMemory()
獲取Java虛擬機(jī)擁有的最大內(nèi)存
虛擬機(jī)的垃圾回收
垃圾回收是指:虛擬機(jī)會(huì)監(jiān)測(cè)應(yīng)用程序的對(duì)象創(chuàng)建和使用,并在一些特定的時(shí)候銷毀無用的對(duì)象以回收內(nèi)存。
垃圾回收的基本想法是要找出虛擬機(jī)中哪些對(duì)象已經(jīng)不會(huì)再被使用然后將其釋放。其最常用的算法有下面兩種:
引用計(jì)數(shù)算法
引用計(jì)數(shù)算法是為每個(gè)對(duì)象維護(hù)一個(gè)被引用的次數(shù):對(duì)象剛創(chuàng)建時(shí)的初始引用計(jì)數(shù)為0,每次被一個(gè)對(duì)象引用時(shí),引用計(jì)數(shù)加1,反之減1。當(dāng)一個(gè)對(duì)象的引用計(jì)數(shù)重新回到0時(shí)便可以認(rèn)為是不會(huì)被使用的,這些對(duì)象便可以被垃圾回收。
讀者可能馬上會(huì)想到,當(dāng)有兩個(gè)對(duì)象互相引用時(shí),這時(shí)引用計(jì)數(shù)該如何計(jì)算。關(guān)于這部分內(nèi)容,這里不再展開講解。有興趣的讀者可以查詢Google或者維基百科:Garbage collection
對(duì)象追蹤算法
對(duì)象追蹤算法是通過GC root類型的對(duì)象為起點(diǎn),追蹤所有被這些對(duì)象所引用的對(duì)象,并順著這些被引用的對(duì)象繼續(xù)往下追蹤,在追蹤的過程中,對(duì)所有被追蹤到的對(duì)象打上標(biāo)記。
而剩下的那些沒有被打過標(biāo)記的對(duì)象便可以認(rèn)為是沒有被使用的,因此這些對(duì)象可以將其釋放。
這里提到的的GC root類型的對(duì)象有四類:
- 棧中的local變量,即方法中的局部變量
- 活動(dòng)的線程(例如主線程或者開發(fā)者創(chuàng)建的線程)
- static變量
- JNI中的引用
下面這幅圖描述了這種算法:
a)表示算法開始時(shí),所有對(duì)象的標(biāo)記為false,然后以GC root為起點(diǎn)開始追蹤和打標(biāo)記,b)中被追蹤到的對(duì)象打上了標(biāo)記。剩下的沒有打上標(biāo)記的對(duì)象便可以釋放了。算法結(jié)束之后,c)中將所有對(duì)象的標(biāo)記全部置為false。下一輪計(jì)算時(shí),重新以GC root開始追蹤。
Dalvik虛擬機(jī)主要用的就是垃圾回收算法,這里是其Source:MarkSweep.cpp
開發(fā)者的內(nèi)存回收
內(nèi)存回收并不是僅僅是系統(tǒng)的事情,作為開發(fā)者,也需要在合適的場(chǎng)合下進(jìn)行內(nèi)存釋放。無節(jié)制的消耗內(nèi)存將導(dǎo)致應(yīng)用程序OutOfMemoryError。
上文中提到,虛擬機(jī)的垃圾回收會(huì)回收那些不會(huì)再被使用到的對(duì)象。因此,開發(fā)者所需要做的就是:當(dāng)確定某些對(duì)象不會(huì)再被使用時(shí),要主動(dòng)釋放對(duì)其引用,這樣虛擬機(jī)才能將其回收。對(duì)于不再被用到對(duì)象,仍然保持對(duì)其引用導(dǎo)致其無法釋放,將導(dǎo)致內(nèi)存泄漏的發(fā)生。
為了更好的進(jìn)行內(nèi)存回收,系統(tǒng)會(huì)一些場(chǎng)景下會(huì)通知應(yīng)用,希望應(yīng)用能夠配合進(jìn)行一些內(nèi)存的釋放。
ComponentCallbacks2
接口中的 void onTrimMemory(int level)
回調(diào)就是用來接收這個(gè)事件的。
Activity
, Service
, ContentProvider
和Application
都實(shí)現(xiàn)了這個(gè)接口,因此這些類的子類都可以接收這個(gè)事件。
onTrimMemory
回調(diào)的參數(shù)是一個(gè)級(jí)別,系統(tǒng)會(huì)根據(jù)應(yīng)用本身的狀態(tài)以及系統(tǒng)的內(nèi)存狀態(tài)發(fā)送不同的級(jí)別,具體的包括:
-
應(yīng)用處于Runnig狀態(tài)可能收到的級(jí)別
-
TRIM_MEMORY_RUNNING_MODERATE
表示系統(tǒng)內(nèi)存已經(jīng)稍低 -
TRIM_MEMORY_RUNNING_LOW
表示系統(tǒng)內(nèi)存已經(jīng)相當(dāng)?shù)?/li> -
TRIM_MEMORY_RUNNING_CRITICAL
表示系統(tǒng)內(nèi)存已經(jīng)非常低,你的應(yīng)用程序應(yīng)當(dāng)考慮釋放部分資源
-
-
應(yīng)用的可見性發(fā)生變化時(shí)收到的級(jí)別
-
TRIM_MEMORY_UI_HIDDEN
表示應(yīng)用已經(jīng)處于不可見狀態(tài),可以考慮釋放一些與顯示相關(guān)的資源
-
-
應(yīng)用處于后臺(tái)時(shí)可能收到的級(jí)別
-
TRIM_MEMORY_BACKGROUND
表示系統(tǒng)內(nèi)存稍低,你的應(yīng)用被殺的可能性不大。但可以考慮適當(dāng)釋放資源 -
TRIM_MEMORY_MODERATE
表示系統(tǒng)內(nèi)存已經(jīng)較低,當(dāng)內(nèi)存持續(xù)減少,你的應(yīng)用可能會(huì)被殺死 -
TRIM_MEMORY_COMPLETE
表示系統(tǒng)內(nèi)存已經(jīng)非常低,你的應(yīng)用即將被殺死,請(qǐng)釋放所有可能釋放的資源
-
這里是這個(gè)方法實(shí)現(xiàn)的示例代碼:Release memory in response to events
在前面的文章中我們提到過:ActivityManagerService
負(fù)責(zé)管理所有的應(yīng)用進(jìn)程。
而這里的通知也是來自ActivityManagerService
。在updateOomAdjLocked
的時(shí)候,ActivityManagerService
會(huì)根據(jù)系統(tǒng)內(nèi)存以及應(yīng)用的狀態(tài)通過app.thread.scheduleTrimMemory
發(fā)送通知給應(yīng)用程序。
這里的app
是ProcessRecord
,即描述應(yīng)用進(jìn)程的對(duì)象,thread
是應(yīng)用的主線程。而scheduleTrimMemory
是通過Binder IPC的方式將消息發(fā)送到應(yīng)用進(jìn)程上。這些內(nèi)容在前面的文章中已經(jīng)介紹過,如果覺得陌生,可以閱讀一下前面兩篇文章。
在ActivityThread
中(這個(gè)是應(yīng)用程序的主線程),接受到這個(gè)通知之后,便會(huì)遍歷應(yīng)用進(jìn)程中所有能接受這個(gè)通知的組件,然后逐個(gè)回調(diào)通知。
相關(guān)代碼如下:
final void handleTrimMemory(int level) {
if (DEBUG_MEMORY_TRIM) Slog.v(TAG, "Trimming memory to level: " + level);
ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(true, null);
final int N = callbacks.size();
for (int i = 0; i < N; i++) {
callbacks.get(i).onTrimMemory(level);
}
WindowManagerGlobal.getInstance().trimMemory(level);
}
Linux OOM Killer
前面提到的機(jī)制都是在進(jìn)程內(nèi)部通過釋放對(duì)象來進(jìn)行內(nèi)存回收。
而實(shí)際上,系統(tǒng)中運(yùn)行的進(jìn)程數(shù)量,以及每個(gè)進(jìn)程所消耗的內(nèi)存都是不確定的。
在極端的情況下,系統(tǒng)的內(nèi)存可能處于非常嚴(yán)峻的狀態(tài),假設(shè)這個(gè)時(shí)候所有進(jìn)程都不愿意釋放內(nèi)存,系統(tǒng)將會(huì)卡死。
為了使系統(tǒng)能夠繼續(xù)運(yùn)轉(zhuǎn)不至于卡死,系統(tǒng)會(huì)嘗試殺死一些不重要的進(jìn)程來進(jìn)行內(nèi)存回收,這其中涉及的模塊主要是:Linux OOM Killer和LowMemoryKiller。
Linux OOM Killer是Linux內(nèi)核的一部分,其源碼可以在這里查看:/mm/oom_kill.c。
Linux OOM Killer的基本想法是:
當(dāng)系統(tǒng)已經(jīng)沒法再分配內(nèi)存的時(shí)候,內(nèi)核會(huì)遍歷所有的進(jìn)程,對(duì)每個(gè)進(jìn)程計(jì)算badness值,得分(badness)最高的進(jìn)程將會(huì)被殺死。
即:badness得分越低表示進(jìn)程越重要,反之表示不重要。
Linux OOM Killer的執(zhí)行流程如下:
_alloc_pages -> out_of_memory() -> select_bad_process() -> oom_badness()
這其中,_alloc_pages
是內(nèi)核在分配內(nèi)存時(shí)調(diào)用的函數(shù)。當(dāng)內(nèi)核發(fā)現(xiàn)無法再分配內(nèi)存時(shí),便會(huì)計(jì)算每個(gè)進(jìn)程的badness值,然后選擇最大的(系統(tǒng)認(rèn)為最不重要的)將其殺死。
那么,內(nèi)核是如何計(jì)算進(jìn)程的badness值的呢?請(qǐng)看下面的代碼:
unsigned long oom_badness(struct task_struct *p, struct mem_cgroup *memcg,
const nodemask_t *nodemask, unsigned long totalpages)
{
long points;
long adj;
...
points = get_mm_rss(p->mm) + p->mm->nr_ptes + get_mm_counter(p->mm, MM_SWAPENTS);
task_unlock(p);
if (has_capability_noaudit(p, CAP_SYS_ADMIN))
points -= (points * 3) / 100;
adj *= totalpages / 1000;
points += adj;
return points > 0 ? points : 1;
}
從這段代碼中,我們可以看到,影響進(jìn)程badness值的因素主要有三個(gè):
- 進(jìn)程的
oom_score_adj
值 - 進(jìn)程的內(nèi)存占用大小
- 進(jìn)程是否是root用戶的進(jìn)程
即,oom_score_adj
(關(guān)于oom_score_adj
,在Android系統(tǒng)中的進(jìn)程管理:進(jìn)程的優(yōu)先級(jí)一文中我們專門講解過。)值越小,進(jìn)程占用的內(nèi)存越小,并且如果是root用戶的進(jìn)程,系統(tǒng)就認(rèn)為這個(gè)進(jìn)程越重要。
反之則被認(rèn)為越不重要,越容易被殺死。
LowMemoryKiller
OOM Killer是在系統(tǒng)內(nèi)存使用情況非常嚴(yán)峻的時(shí)候才會(huì)起作用。但直到這個(gè)時(shí)候才開始?xì)⑺肋M(jìn)程來回收內(nèi)存是有點(diǎn)晚的。因?yàn)樵谶M(jìn)程被殺死之前,其他進(jìn)程都無法再申請(qǐng)內(nèi)存了。
因此,Google在Android上新增了一個(gè)LowMemoryKiller模塊。LowMemoryKiller通常會(huì)在Linux OOM Killer工作之前,就開始?xì)⑺肋M(jìn)程。
LowMemoryKiller的做法是:
提供6個(gè)可以設(shè)置的內(nèi)存級(jí)別,當(dāng)系統(tǒng)內(nèi)存每低于一個(gè)級(jí)別時(shí),將oom_score_adj大于某個(gè)指定值的進(jìn)程全部殺死。
這么說會(huì)有些抽象,但具體看一下LowMemoryKiller的配置文件我們就好理解了。
LowMemoryKiller在sysfs上暴露了兩個(gè)文件來供系統(tǒng)調(diào)整參數(shù),這兩個(gè)文件的路徑是:
/sys/module/lowmemorykiller/parameters/minfree
/sys/module/lowmemorykiller/parameters/adj
如果你手上有一個(gè)Android設(shè)備,你可以通過adb shell
連上去之后,通過cat
命令查看這兩個(gè)文件的內(nèi)容。
這兩個(gè)文件是配對(duì)使用的,每個(gè)文件中都是由逗號(hào)分隔的6個(gè)整數(shù)值。
在某個(gè)設(shè)備上,這兩個(gè)文件的值可能分別是下面這樣:
18432,23040,27648,32256,55296,80640
0,100,200,300,900,906
這組配置的含義是;當(dāng)系統(tǒng)內(nèi)存低于80640k時(shí),將oom_score_adj值大于906的進(jìn)程全部殺死;當(dāng)系統(tǒng)內(nèi)存低于55296k時(shí),將oom_score_adj值大于900的進(jìn)程全部殺死,其他類推。
LowMemoryKiller殺死進(jìn)程的時(shí)候會(huì)在內(nèi)核留下日志,你可以通過dmesg
命令中看到。這個(gè)日志可能是這樣的:
lowmemorykiller: Killing 'gnunet-service-' (service adj 0,
to free 327224kB on behalf of 'kswapd0' (21) because
cache 6064kB is below limit 6144kB for oom_score_adj 0
從這個(gè)日志中,我們可以看到被殺死進(jìn)程的名稱,進(jìn)程pid和oom_score_adj值。另外還有系統(tǒng)在殺死這個(gè)進(jìn)程之前系統(tǒng)內(nèi)存還剩多少,以及殺死這個(gè)進(jìn)程釋放了多少。
LowMemoryKiller的源碼也在內(nèi)核中,路徑是:kernel/drivers/staging/android/lowmemorykiller.c
。
lowmemorykiller.c中定義了如下幾個(gè)函數(shù):
lowmem_shrink
lowmem_init
lowmem_exit
lowmem_oom_adj_to_oom_score_adj
lowmem_autodetect_oom_adj_values
lowmem_adj_array_set
lowmem_adj_array_get
lowmem_adj_array_free
LowMemoryKiller本身是一個(gè)內(nèi)核驅(qū)動(dòng)程序的形式存在,lowmem_init
和lowmem_exit
分別負(fù)責(zé)模塊的初始化和退出清理工作。
在lowmem_init
函數(shù)中,就是通過register_shrinker
向內(nèi)核中注冊(cè)了register_shrinker
函數(shù):
static int __init lowmem_init(void)
{
register_shrinker(&lowmem_shrinker);
return 0;
}
register_shrinker
函數(shù)就是LowMemoryKiller的算法核心,這個(gè)函數(shù)的代碼和說明如下:
static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc)
{
struct task_struct *tsk;
struct task_struct *selected = NULL;
int rem = 0;
int tasksize;
int i;
short min_score_adj = OOM_SCORE_ADJ_MAX + 1;
int minfree = 0;
int selected_tasksize = 0;
short selected_oom_score_adj;
int array_size = ARRAY_SIZE(lowmem_adj);
int other_free = global_page_state(NR_FREE_PAGES) - totalreserve_pages;
int other_file = global_page_state(NR_FILE_PAGES) -
global_page_state(NR_SHMEM) -
total_swapcache_pages();
if (lowmem_adj_size < array_size)
array_size = lowmem_adj_size;
if (lowmem_minfree_size < array_size)
array_size = lowmem_minfree_size;
// lowmem_minfree 和lowmem_adj 記錄了兩個(gè)配置文件中配置的數(shù)據(jù)
for (i = 0; i < array_size; i++) {
minfree = lowmem_minfree[i];
// 確定當(dāng)前系統(tǒng)處于低內(nèi)存的第幾檔
if (other_free < minfree && other_file < minfree) {
// 確定需要?dú)⑺赖倪M(jìn)程的oom_score_adj的上限
min_score_adj = lowmem_adj[i];
break;
}
}
if (sc->nr_to_scan > 0)
lowmem_print(3, "lowmem_shrink %lu, %x, ofree %d %d, ma %hd\n",
sc->nr_to_scan, sc->gfp_mask, other_free,
other_file, min_score_adj);
rem = global_page_state(NR_ACTIVE_ANON) +
global_page_state(NR_ACTIVE_FILE) +
global_page_state(NR_INACTIVE_ANON) +
global_page_state(NR_INACTIVE_FILE);
if (sc->nr_to_scan <= 0 || min_score_adj == OOM_SCORE_ADJ_MAX + 1) {
lowmem_print(5, "lowmem_shrink %lu, %x, return %d\n",
sc->nr_to_scan, sc->gfp_mask, rem);
return rem;
}
selected_oom_score_adj = min_score_adj;
rcu_read_lock();
// 遍歷所有進(jìn)程
for_each_process(tsk) {
struct task_struct *p;
short oom_score_adj;
if (tsk->flags & PF_KTHREAD)
continue;
p = find_lock_task_mm(tsk);
if (!p)
continue;
if (test_tsk_thread_flag(p, TIF_MEMDIE) &&
time_before_eq(jiffies, lowmem_deathpending_timeout)) {
task_unlock(p);
rcu_read_unlock();
return 0;
}
oom_score_adj = p->signal->oom_score_adj;
// 跳過那些oom_score_adj值比目標(biāo)值小的
if (oom_score_adj < min_score_adj) {
task_unlock(p);
continue;
}
tasksize = get_mm_rss(p->mm);
task_unlock(p);
if (tasksize <= 0)
continue;
// selected 是將要?dú)⑺赖膫溥x進(jìn)程
if (selected) {
// 跳過那些oom_score_adj比備選的小的
if (oom_score_adj < selected_oom_score_adj)
continue;
// 如果oom_score_adj一樣,跳過那些內(nèi)存消耗更小的
if (oom_score_adj == selected_oom_score_adj &&
tasksize <= selected_tasksize)
continue;
}
// 更換備選的目標(biāo),因?yàn)橛职l(fā)現(xiàn)了一個(gè)oom_score_adj更大,
// 或者內(nèi)存消耗更大的進(jìn)程
selected = p;
selected_tasksize = tasksize;
selected_oom_score_adj = oom_score_adj;
lowmem_print(2, "select '%s' (%d), adj %hd, size %d, to kill\n",
p->comm, p->pid, oom_score_adj, tasksize);
}
// 已經(jīng)選中目標(biāo),記錄日志并殺死進(jìn)程
if (selected) {
long cache_size = other_file * (long)(PAGE_SIZE / 1024);
long cache_limit = minfree * (long)(PAGE_SIZE / 1024);
long free = other_free * (long)(PAGE_SIZE / 1024);
trace_lowmemory_kill(selected, cache_size, cache_limit, free);
lowmem_print(1, "Killing '%s' (%d), adj %hd,\n" \
" to free %ldkB on behalf of '%s' (%d) because\n" \
" cache %ldkB is below limit %ldkB for oom_score_adj %hd\n" \
" Free memory is %ldkB above reserved\n",
selected->comm, selected->pid,
selected_oom_score_adj,
selected_tasksize * (long)(PAGE_SIZE / 1024),
current->comm, current->pid,
cache_size, cache_limit,
min_score_adj,
free);
lowmem_deathpending_timeout = jiffies + HZ;
set_tsk_thread_flag(selected, TIF_MEMDIE);
send_sig(SIGKILL, selected, 0);
rem -= selected_tasksize;
}
lowmem_print(4, "lowmem_shrink %lu, %x, return %d\n",
sc->nr_to_scan, sc->gfp_mask, rem);
rcu_read_unlock();
return rem;
}
進(jìn)程的死亡處理
在任何時(shí)候,應(yīng)用進(jìn)程都可能死亡,例如被OOM Killer或者LowMemoryKiller殺死,自身crash死亡又或者被用戶手動(dòng)殺死。無論哪種情況,作為應(yīng)用進(jìn)程的管理者ActivityManagerService
都需要知道。
在應(yīng)用進(jìn)程死亡之后,ActivityManagerService需要執(zhí)行如下工作:
- 執(zhí)行清理工作 ActivityManagerService內(nèi)部的ProcessRecord以及可能存在的四大組件的相關(guān)結(jié)構(gòu)需要全部清理干凈
- 重新計(jì)算進(jìn)程的優(yōu)先級(jí) 上文已經(jīng)提到過,進(jìn)程的優(yōu)先級(jí)是有關(guān)聯(lián)性的,有其中一個(gè)進(jìn)程死亡了,可能會(huì)連到影響到其他進(jìn)程的優(yōu)先級(jí)需要調(diào)整。
ActivityManagerService是利用Binder提供的死亡通知機(jī)制來進(jìn)行進(jìn)程的死亡處理的。關(guān)于Binder請(qǐng)參閱其他資料,限于篇幅關(guān)系,這里不再展開講解。
簡(jiǎn)單來說,死亡通知機(jī)制就提供了進(jìn)程間的一種死亡監(jiān)聽的能力:當(dāng)目標(biāo)進(jìn)程死亡的時(shí)候,監(jiān)聽回調(diào)會(huì)執(zhí)行。
ActivityManagerService中的AppDeathRecipient監(jiān)聽了應(yīng)用進(jìn)程的死亡消息,該類代碼如下:
private final class AppDeathRecipient implements IBinder.DeathRecipient {
final ProcessRecord mApp;
final int mPid;
final IApplicationThread mAppThread;
AppDeathRecipient(ProcessRecord app, int pid,
IApplicationThread thread) {
mApp = app;
mPid = pid;
mAppThread = thread;
}
@Override
public void binderDied() {
synchronized(ActivityManagerService.this) {
appDiedLocked(mApp, mPid, mAppThread, true);
}
}
}
每一個(gè)應(yīng)用進(jìn)程在啟動(dòng)之后,都會(huì)attach到ActivityManagerService上通知它自己的進(jìn)程已經(jīng)啟動(dòng)完成了。這時(shí)ActivityManagerService便會(huì)為其創(chuàng)建一個(gè)死亡通知的監(jiān)聽器。在這之后如果進(jìn)程死亡了,ActivityManagerService便會(huì)收到通知。
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
...
try {
AppDeathRecipient adr = new AppDeathRecipient(
app, pid, thread);
thread.asBinder().linkToDeath(adr, 0);
app.deathRecipient = adr;
} catch (RemoteException e) {
app.resetPackageList(mProcessStats);
startProcessLocked(app, "link fail", processName);
return false;
}
...
}
進(jìn)程死亡之后的處理工作是appDiedLocked
這個(gè)方法中處理的,這部分還是比較容易理解的,這里就不過多講解了。
結(jié)束語
這三篇文章,我們?cè)敿?xì)講解了Android系統(tǒng)中進(jìn)程的創(chuàng)建,優(yōu)先級(jí)的管理和內(nèi)存回收。這些內(nèi)容對(duì)于所有運(yùn)行在Android系統(tǒng)中的應(yīng)用進(jìn)程都是適用的。
優(yōu)秀的開發(fā)者應(yīng)該充分了解這些內(nèi)容,因?yàn)檫@是與應(yīng)用的生命周期密切相關(guān)的。
由于篇幅所限,這其中有些知識(shí)我們沒有詳細(xì)展開討論,但有些內(nèi)容會(huì)在今后的文章中專門講解。
由于筆者水平有限,文章中不免有所錯(cuò)漏,歡迎讀者指出。
參考資料與推薦讀物
Overview of Android Memory Management
Understanding Java Garbage Collection
Debugging ART Garbage Collection