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
Out Of?Memory Management