Android進(jìn)程管理三部曲[3]-內(nèi)存的回收

作者: 強(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, ContentProviderApplication都實(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)用程序。

這里的appProcessRecord,即描述應(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_initlowmem_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

Processes and Threads

Java Memory Management

Debugging ART Garbage Collection

Android Runtime

Taming the OOM killer

OOM Killer

Out Of Memory Management

更多文章請(qǐng)關(guān)注公眾號(hào)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容