【Android開發(fā)高級(jí)系列】?jī)?nèi)存管理專題

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

http://www.reibang.com/p/c170f173de01


1 概述

????????對(duì)于內(nèi)存回收,主要可以分為兩個(gè)層次:

? ??進(jìn)程內(nèi)的內(nèi)存回收:通過(guò)釋放進(jìn)程中的資源進(jìn)行內(nèi)存回收隔崎;

? ??進(jìn)程級(jí)的內(nèi)存回收:通過(guò)殺死進(jìn)程來(lái)進(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ì)通過(guò)殺死進(jìn)程來(lái)進(jìn)行內(nèi)存回收。

1.1 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ī)宫患,稱作AndroidRuntime,簡(jiǎn)稱“ART”这弧。

????????關(guān)于ART和Dalvik可以參見這里:ART and Dalvik娃闲。無(wú)論是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出來(lái)的子進(jìn)程礼预。關(guān)于這部分內(nèi)容眠砾,我們已經(jīng)在Android系統(tǒng)中的進(jìn)程管理:進(jìn)程的創(chuàng)建一文中講解過(guò)。

????????在Java語(yǔ)言中托酸,通過(guò)new創(chuàng)建的對(duì)象都會(huì)在堆中分配內(nèi)存褒颈。應(yīng)用程序堆的大小是有限的。系統(tǒng)會(huì)根據(jù)設(shè)備的物理內(nèi)存大小來(lái)確定每個(gè)應(yīng)用程序所允許使用的內(nèi)存大小励堡,一旦應(yīng)用程序使用的內(nèi)存超過(guò)這個(gè)大小谷丸,便會(huì)發(fā)生OutOfMemoryError

????????因此開發(fā)者需要關(guān)心應(yīng)用的內(nèi)存使用狀況应结。關(guān)于如何監(jiān)測(cè)應(yīng)用程序的內(nèi)存使用刨疼,可以參見這里:Investigating Your RAM Usage

2 開發(fā)者相關(guān)的API

????????下面是一些與內(nèi)存相關(guān)的開發(fā)者API鹅龄,它們是AndroidSDK的一部分揩慕。

2.1 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)釋放一定的資源來(lái)緩解內(nèi)存緊張的狀態(tài)八堡。

????????ComponentCallbacks2接口中的void onTrimMemory(int level)回調(diào)函數(shù)用來(lái)接收這個(gè)通知樟凄。關(guān)于這一點(diǎn),在“開發(fā)者的內(nèi)存回收”一節(jié)兄渺,我們會(huì)詳細(xì)講解不同。

2.2 ActivityManager

????????ActivityManager,從名稱中就可以看出溶耘,這個(gè)類是用來(lái)管理Activity的系統(tǒng)服務(wù)。但這個(gè)類中也包含了很多運(yùn)行時(shí)狀態(tài)查詢的接口服鹅,這其中就包括與內(nèi)存相關(guān)的幾個(gè):

? ? 1凳兵、int getMemoryClass ()獲取當(dāng)前設(shè)備上,單個(gè)應(yīng)用的內(nèi)存大小限制企软,單位是M庐扫。注意,這個(gè)函數(shù)的返回值只是一個(gè)大致的值。

? ? 2形庭、void getMemoryInfo (ActivityManager.MemoryInfo outInfo)獲取系統(tǒng)的內(nèi)存信息铅辞,具體結(jié)構(gòu)可以查看ActivityManager.MemoryInfo,開發(fā)者最關(guān)心的可能就是availMem以及totalMem萨醒。

? ? 3斟珊、void getMyMemoryState (ActivityManager.RunningAppProcessInfo outState)獲取調(diào)用進(jìn)程的內(nèi)存信息;

? ? 4富纸、MemoryInfo[] getProcessMemoryInfo (int[] pids)通過(guò)pid獲取指定進(jìn)程的內(nèi)存信息囤踩;

? ? 5、boolean isLowRamDevice()查詢當(dāng)前設(shè)備是否是低內(nèi)存設(shè)備晓褪;

2.3 Runtime

????????Java應(yīng)用程序都會(huì)有一個(gè)Runtime接口的實(shí)例堵漱,通過(guò)這個(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)存;

3 虛擬機(jī)的垃圾回收

????????垃圾回收是指:虛擬機(jī)會(huì)監(jiān)測(cè)應(yīng)用程序的對(duì)象創(chuàng)建和使用好港,并在一些特定的時(shí)候銷毀無(wú)用的對(duì)象以回收內(nèi)存愉镰。

????????垃圾回收的基本想法是要找出虛擬機(jī)中哪些對(duì)象已經(jīng)不會(huì)再被使用然后將其釋放。其最常用的算法有下面兩種:

3.1 引用計(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

3.2 對(duì)象追蹤算法

????????對(duì)象追蹤算法是通過(guò)GC root類型的對(duì)象為起點(diǎn),追蹤所有被這些對(duì)象所引用的對(duì)象赋兵,并順著這些被引用的對(duì)象繼續(xù)往下追蹤笔咽,在追蹤的過(guò)程中,對(duì)所有被追蹤到的對(duì)象打上標(biāo)記霹期。而剩下的那些沒有被打過(guò)標(biāo)記的對(duì)象便可以認(rèn)為是沒有被使用的叶组,因此這些對(duì)象可以將其釋放。

????????這里提到的的GC root類型的對(duì)象有四類:

? ? 1历造、棧中的local變量甩十,即方法中的局部變量

? ? 2船庇、活動(dòng)的線程(例如主線程或者開發(fā)者創(chuàng)建的線程)

? ? 3、static變量

? ? 4侣监、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ī)主要用的就是對(duì)象追蹤算法刻剥,這里是其Source:MarkSweep.cpp

4 開發(fā)者的內(nèi)存回收處理

????????內(nèi)存回收并不是僅僅是系統(tǒng)的事情,作為開發(fā)者滩字,也需要在合適的場(chǎng)合下進(jìn)行內(nèi)存釋放造虏。無(wú)節(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)致其無(wú)法釋放栗竖,將導(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(intlevel)回調(diào)就是用來(lái)接收這個(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)用處于Running狀態(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ù)停?/p>

??????????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

????????在前面的文章中我們提到過(guò):ActivityManagerService負(fù)責(zé)管理所有的應(yīng)用進(jìn)程往声。而這里的通知也是來(lái)自ActivityManagerService。在updateOomAdjLocked的時(shí)候戳吝,ActivityManagerService會(huì)根據(jù)系統(tǒng)內(nèi)存以及應(yīng)用的狀態(tài)通過(guò)app.thread.scheduleTrimMemory發(fā)送通知給應(yīng)用程序浩销。這里的app是ProcessRecord,即描述應(yīng)用進(jìn)程的對(duì)象听哭,thread是應(yīng)用的主線程慢洋。而scheduleTrimMemory是通過(guò)Binder IPC的方式將消息發(fā)送到應(yīng)用進(jìn)程上。這些內(nèi)容在前面的文章中已經(jīng)介紹過(guò)陆盘,如果覺得陌生普筹,可以閱讀一下前面兩篇文章。

????????在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 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);

}

4.1 Linux OOM Killer

????????前面提到的機(jī)制都是在進(jìn)程內(nèi)部通過(guò)釋放對(duì)象來(lái)進(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)程來(lái)進(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)無(wú)法再分配內(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í)一文中我們專門講解過(guò)撞叽。)值越小姻成,進(jìn)程占用的內(nèi)存越小,并且如果是root用戶的進(jìn)程愿棋,系統(tǒng)就認(rèn)為這個(gè)進(jìn)程越重要科展。反之則被認(rèn)為越不重要,越容易被殺死糠雨。

4.2 LowMemoryKiller

????????OOM Killer是在系統(tǒng)內(nèi)存使用情況非常嚴(yán)峻的時(shí)候才會(huì)起作用才睹。但直到這個(gè)時(shí)候才開始?xì)⑺肋M(jìn)程來(lái)回收內(nèi)存是有點(diǎn)晚的。因?yàn)樵谶M(jìn)程被殺死之前甘邀,其他進(jìn)程都無(wú)法再申請(qǐng)內(nèi)存了琅攘。因此,Google在Android上新增了一個(gè)LowMemoryKiller模塊松邪。LowMemoryKiller通常會(huì)在Linux OOMKiller工作之前坞琴,就開始?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)程全部殺死。這么說(shuō)會(huì)有些抽象邮府,但具體看一下LowMemoryKiller的配置文件我們就好理解了荧关。

????????LowMemoryKiller在sysfs上暴露了兩個(gè)文件來(lái)供系統(tǒng)調(diào)整參數(shù),這兩個(gè)文件的路徑是:

????/sys/module/lowmemorykiller/parameters/minfree

????/sys/module/lowmemorykiller/parameters/adj

????????如果你手上有一個(gè)Android設(shè)備褂傀,你可以通過(guò)adb shell連上去之后忍啤,通過(guò)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)核留下日志吆玖,你可以通過(guò)dmesg命令中看到筒溃。這個(gè)日志可能是這樣的:

lowmemorykiller: Killing 'gnunet-service-' (service adj 0, to free 327224kB on behalf of 'kswapd0' (21) because cache 6064kB is below limit 6144kB foroom_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ù)中,就是通過(guò)register_shrinker向內(nèi)核中注冊(cè)了register_shrinker函數(shù):

static int lowmem_init(void)

{

??? register_shrinker(&lowmem_shrinker);

??? return 0;

}

????????register_shrinker函數(shù)就是LowMemoryKiller的算法核心讹语,這個(gè)函數(shù)的代碼和說(shuō)明如下:

static int lowmem_shrink(structshrinker *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();

????????????return0;

????????}

????????oom_score_adj = p->signal->oom_score_adj;// 跳過(guò)那些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) {

????????????// 跳過(guò)那些oom_score_adj比備選的小的

????????????if(oom_score_adj < selected_oom_score_adj)

? ? ? ? ? ? ? ? continue;//如果oom_score_adj一樣钙皮,跳過(guò)那些內(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;

}


4.3 進(jìn)程的死亡處理

????????在任何時(shí)候,應(yīng)用進(jìn)程都可能死亡才菠,例如被OOM Killer或者LowMemoryKiller殺死茸时,自身crash死亡又或者被用戶手動(dòng)殺死。無(wú)論哪種情況赋访,作為應(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)提到過(guò)进每,進(jìn)程的優(yōu)先級(jí)是有關(guān)聯(lián)性的汹粤,有其中一個(gè)進(jìn)程死亡了,可能會(huì)連到影響到其他進(jìn)程的優(yōu)先級(jí)需要調(diào)整田晚。

????????ActivityManagerService是利用Binder提供的死亡通知機(jī)制來(lái)進(jìn)行進(jìn)程的死亡處理的嘱兼。關(guān)于Binder請(qǐng)參閱其他資料,限于篇幅關(guān)系贤徒,這里不再展開講解芹壕。

????????簡(jiǎn)單來(lái)說(shuō)汇四,死亡通知機(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 ApplicationThread 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 = newAppDeathRecipient(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è)方法中處理的行剂,這部分還是比較容易理解的,這里就不過(guò)多講解了钳降。

5 參考鏈接

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

http://www.reibang.com/p/c170f173de01


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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末厚宰,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子遂填,更是在濱河造成了極大的恐慌铲觉,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吓坚,死亡現(xiàn)場(chǎng)離奇詭異撵幽,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)凌唬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門并齐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人客税,你說(shuō)我怎么就攤上這事况褪。” “怎么了更耻?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵测垛,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我秧均,道長(zhǎng)食侮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任目胡,我火速辦了婚禮锯七,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘誉己。我一直安慰自己眉尸,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著噪猾,像睡著了一般霉祸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上袱蜡,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天丝蹭,我揣著相機(jī)與錄音,去河邊找鬼坪蚁。 笑死奔穿,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的敏晤。 我是一名探鬼主播巫橄,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼茵典!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起宾舅,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤统阿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后筹我,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體扶平,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年蔬蕊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了结澄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡岸夯,死狀恐怖麻献,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情猜扮,我是刑警寧澤勉吻,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站旅赢,受9級(jí)特大地震影響齿桃,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜煮盼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一短纵、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧僵控,春花似錦香到、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)雷绢。三九已至,卻和暖如春理卑,著一層夾襖步出監(jiān)牢的瞬間翘紊,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工藐唠, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留帆疟,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓宇立,卻偏偏與公主長(zhǎng)得像踪宠,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子妈嘹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

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

  • Android 的設(shè)計(jì)理念之一柳琢,便是應(yīng)用程序退出,但進(jìn)程還會(huì)繼續(xù)存在系統(tǒng)以便再次啟動(dòng)時(shí)提高響應(yīng)時(shí)間. 這樣的設(shè)計(jì)會(huì)...
    tiger桂閱讀 1,781評(píng)論 0 1
  • 本篇文章是后臺(tái)殺死系列的最后一篇,主要探討一下進(jìn)程的北郏活垦巴,Android本身設(shè)計(jì)的時(shí)候是非常善良的,它希望進(jìn)程在不...
    看書的小蝸牛閱讀 11,670評(píng)論 10 66
  • 作者: 強(qiáng)波 (阿里云OS平臺(tái)部-Cloud Engine)博客: http://qiangbo.space/...
    菜刀文閱讀 1,858評(píng)論 3 9
  • 此間月 縈繞著你我 墜落 夢(mèng)中的婚禮 沒有回音 寂靜 你我之間 無(wú)法自拔的相愛相殺 離別 重來(lái)成惘然
    未明花香閱讀 200評(píng)論 0 1
  • 生活中令人抓狂的铭段,往往都是一些看似微不足道的小事骤宣。 比如,零星散落在地毯上的狗毛稠项,昨晚吃完飯忘記洗的臟碗筷涯雅,一封不...
    凌正元子閱讀 263評(píng)論 0 0