Android 內(nèi)存回收

一劣摇、垃圾回收(GC):
Java垃圾回收器:

在C刷后,C++或其他程序設(shè)計(jì)語言中吃靠,資源或內(nèi)存都必須由程序員自行聲明產(chǎn)生和回收硫眨,否則其中的資源將消耗,造成資源的浪費(fèi)甚至崩潰巢块。但手工回收內(nèi)存往往是一項(xiàng)復(fù)雜而艱巨的工作礁阁。
于是,Java技術(shù)提供了一個(gè)系統(tǒng)級(jí)的線程族奢,即垃圾收集器線程(Garbage Collection Thread)氮兵,來跟蹤每一塊分配出去的內(nèi)存空間,當(dāng)Java 虛擬機(jī)(Java Virtual Machine)處于空閑循環(huán)時(shí)歹鱼,垃圾收集器線程會(huì)自動(dòng)檢查每一快分配出去的內(nèi)存空間,然后自動(dòng)回收每一快可以回收的無用的內(nèi)存塊卜高。

垃圾回收器優(yōu)點(diǎn):
  • 1.減輕編程的負(fù)擔(dān)弥姻,提高效率:
    使程序員從手工回收內(nèi)存空間的繁重工作中解脫了出來,因?yàn)樵跊]有垃圾收集機(jī)制的時(shí)候掺涛,可能要花許多時(shí)間來解決一個(gè)難懂的存儲(chǔ)器問題庭敦。在用Java語言編程的時(shí)候,靠垃圾收集機(jī)制可大大縮短時(shí)間薪缆。

  • 2.它保護(hù)程序的完整性:
    因此垃圾收集是Java語言安全性策略的一個(gè)重要部份秧廉。

垃圾回收器缺點(diǎn):
  • 1.占用資源時(shí)間:
    Java虛擬機(jī)必須追蹤運(yùn)行程序中有用的對(duì)象, 而且最終釋放沒用的對(duì)象。這一個(gè)過程需要花費(fèi)處理器的時(shí)間拣帽。

  • 2.不可預(yù)知:
    垃圾收集器線程雖然是作為低優(yōu)先級(jí)的線程運(yùn)行疼电,但在系統(tǒng)可用內(nèi)存量過低的時(shí)候,它可能會(huì)突發(fā)地執(zhí)行來挽救內(nèi)存資源减拭。當(dāng)然其執(zhí)行與否也是不可預(yù)知的蔽豺。

  • 3.不確定性:
    不能保證一個(gè)無用的對(duì)象一定會(huì)被垃圾收集器收集,也不能保證垃圾收集器在一段Java語言代碼中一定會(huì)執(zhí)行拧粪。同樣也沒有辦法預(yù)知在一組均符合垃圾收集器收集標(biāo)準(zhǔn)的對(duì)象中修陡,哪一個(gè)會(huì)被首先收集沧侥。

  • 4.不可操作
    垃圾收集器不可以被強(qiáng)制執(zhí)行,但程序員可以通過調(diào)用System. gc方法來建議執(zhí)行垃圾收集器魄鸦。

垃圾回收算法:
  • 1.引用計(jì)數(shù)(Reference Counting)
    比較古老的回收算法宴杀。原理是此對(duì)象有一個(gè)引用,即增加一個(gè)計(jì)數(shù)拾因,刪除一個(gè)引用則減少一個(gè)計(jì)數(shù)旺罢。垃圾回收時(shí),只用收集計(jì)數(shù)為0的對(duì)象盾致。此算法最致命的是無法處理循環(huán)引用的問題主经。

  • 2.標(biāo)記-清除(Mark-Sweep)
    此算法執(zhí)行分兩階段。第一階段從引用根節(jié)點(diǎn)開始標(biāo)記所有被引用的對(duì)象庭惜,第二階段遍歷整個(gè)堆罩驻,把未標(biāo)記的對(duì)象清除。此算法需要暫停整個(gè)應(yīng)用护赊,同時(shí)惠遏,會(huì)產(chǎn)生內(nèi)存碎片。

  • 3.復(fù)制(Copying)
    此算法把內(nèi)存空間劃為兩個(gè)相等的區(qū)域骏啰,每次只使用其中一個(gè)區(qū)域节吮。垃圾回收時(shí),遍歷當(dāng)前使用區(qū)域判耕,把正在使用中的對(duì)象復(fù)制到另外一個(gè)區(qū)域中透绩。次算法每次只處理正在使用中的對(duì)象,因此復(fù)制成本比較小壁熄,同時(shí)復(fù)制過去以后還能進(jìn)行相應(yīng)的內(nèi)存整理帚豪,不過出現(xiàn)“碎片”問題。當(dāng)然草丧,此算法的缺點(diǎn)也是很明顯的狸臣,就是需要兩倍內(nèi)存空間。

  • 4.標(biāo)記-整理(Mark-Compact)
    此算法結(jié)合了 “標(biāo)記-清除”和“復(fù)制”兩個(gè)算法的優(yōu)點(diǎn)昌执。也是分兩階段烛亦,第一階段從根節(jié)點(diǎn)開始標(biāo)記所有被引用對(duì)象,第二階段遍歷整個(gè)堆懂拾,把清除未標(biāo)記對(duì)象并且把存活對(duì)象 “壓縮”到堆的其中一塊煤禽,按順序排放。此算法避免了“標(biāo)記-清除”的碎片問題岖赋,同時(shí)也避免了“復(fù)制”算法的空間問題呜师。

  • 5.增量收集(Incremental Collecting)
    實(shí)施垃圾回收算法,即:在應(yīng)用進(jìn)行的同時(shí)進(jìn)行垃圾回收贾节。不知道什么原因JDK5.0中的收集器沒有使用這種算法的汁汗。

  • 6.分代(Generational Collecting)
    基于對(duì)對(duì)象生命周期分析后得出的垃圾回收算法衷畦。把對(duì)象分為年青代、年老代知牌、持久代祈争,對(duì)不同生命周期的對(duì)象使用不同的算法(上述方式中的一個(gè))進(jìn)行回收。現(xiàn)在的垃圾回收器(從J2SE1.2開始)都是使用此算法的角寸。

finalize():

每一個(gè)對(duì)象都有一個(gè)finalize方法菩混,這個(gè)方法是從Object類繼承來的。
當(dāng)垃圾回收確定不存在對(duì)該對(duì)象的更多引用時(shí)扁藕,由對(duì)象的垃圾回收器調(diào)用此方法沮峡。

Java 技術(shù)允許使用finalize方法在垃圾收集器將對(duì)象從內(nèi)存中清除出去之前做必要的清理工作。一旦垃圾回收器準(zhǔn)備好釋放對(duì)象占用的空間亿柑,將首先調(diào)用其finalize()方法邢疙,并且在下一次垃圾回收動(dòng)作發(fā)生時(shí)炮障,才會(huì)真正回收對(duì)象占用的內(nèi)存佣蓉。
簡單的說finalize方法是在垃圾收集器刪除對(duì)象之前對(duì)這個(gè)對(duì)象調(diào)用的

System.gc():

我們可以調(diào)用System.gc方法,建議虛擬機(jī)進(jìn)行垃圾回收工作(注意莱睁,是建議痕支,但虛擬機(jī)會(huì)不會(huì)這樣干颁虐,我們也無法預(yù)知!)


一卧须、Android APP 的運(yùn)行環(huán)境

Android 是一款基于 Linux 內(nèi)核另绩,面向移動(dòng)終端的操作系統(tǒng)。為適應(yīng)其作為移動(dòng)平臺(tái)操作系統(tǒng)的特殊需要花嘶,谷歌對(duì)其做了特別的設(shè)計(jì)與優(yōu)化板熊,使得其進(jìn)程調(diào)度與資源管理與其他平臺(tái)的 Linux 有明顯的區(qū)別。主要包含下面幾個(gè)層次:

1察绷、Application Framework

Application Framework 將整個(gè)操作系統(tǒng)分隔成兩個(gè)部分。對(duì)應(yīng)用開發(fā)者而言津辩,所有 APP 都是運(yùn)行在 Application Framework 之上拆撼,而并不需要關(guān)心系統(tǒng)底層的情況。Application Framework 層為應(yīng)用開發(fā)者提供了豐富的應(yīng)用編程接口喘沿,如 Activity Manager闸度,Content Provider,Notification Manager蚜印,以及各種窗口 Widget 資源等莺禁。在 Application Framework 層,Activity 是一個(gè) APP 最基本的組成部分窄赋。一般每個(gè) Activity 對(duì)應(yīng)于屏幕上的一個(gè)視圖(或者說一屏)哟冬,一個(gè) APP 可以有一個(gè)或者多個(gè) Activity楼熄。應(yīng)用程序被打包成 .apk 格式的文件,由 Dalvik VM 解釋執(zhí)行浩峡。

2可岂、Dalvik VM && Android Runtime(ART)

Dalvik 虛擬機(jī)采用寄存器架構(gòu),而不是 JVM 的棧結(jié)構(gòu)翰灾。Java 程序編譯后的 .class 文件并不能在 Dalvik 中解釋執(zhí)行缕粹。因此 Google 提供了一個(gè) dx 工具,用于將 .class 文件轉(zhuǎn)換成 Dalivk 能夠識(shí)別的 .dex 格式纸淮。
Dalvik VM 效率并不是最高的平斩。從 Android 4.4 開始,Google 開發(fā)者引進(jìn)了新的 Android 運(yùn)行環(huán)境 ART咽块,從Android 5.0系統(tǒng)開始绘面,安卓系統(tǒng)徹底從Dalvik轉(zhuǎn)換到ART。

3糜芳、Linux kernel

由上所述飒货,所有的 APP 都是由 Java 代碼編寫并在 Dalvik VM 中得到解釋執(zhí)行。在 Android 操作系統(tǒng)中峭竣,每個(gè) Dalvik VM 的每個(gè) Instance 都對(duì)應(yīng)于 Linux 內(nèi)核中的一個(gè)進(jìn)程塘辅。可以使用 adb shell 工具查看系統(tǒng)中的當(dāng)前進(jìn)程皆撩。

二扣墩、Android 內(nèi)存回收原則

下面將從 Application Framework 和 Linux kernel 兩個(gè)層次分析 Android 操作系統(tǒng)的資源管理機(jī)制。
Android 之所以采用特殊的資源管理機(jī)制扛吞,原因在于其設(shè)計(jì)之初就是面向移動(dòng)終端呻惕,所有可用的內(nèi)存僅限于系統(tǒng) RAM,必須針對(duì)這種限制設(shè)計(jì)相應(yīng)的優(yōu)化方案滥比。當(dāng) Android 應(yīng)用程序退出時(shí)亚脆,并不清理其所占用的內(nèi)存,Linux 內(nèi)核進(jìn)程也相應(yīng)的繼續(xù)存在盲泛,所謂“退出但不關(guān)閉”濒持。從而使得用戶調(diào)用程序時(shí)能夠在第一時(shí)間得到響應(yīng)。當(dāng)系統(tǒng)內(nèi)存不足時(shí)寺滚,系統(tǒng)將激活內(nèi)存回收過程柑营。為了不因內(nèi)存回收影響用戶體驗(yàn)(如殺死當(dāng)前的活動(dòng)進(jìn)程),Android 基于進(jìn)程中運(yùn)行的組件及其狀態(tài)規(guī)定了默認(rèn)的五個(gè)回收優(yōu)先級(jí):

  • IMPORTANCE_FOREGROUND:前臺(tái)進(jìn)程
  • IMPORTANCE_VISIBLE:可見進(jìn)程
  • IMPORTANCE_SERVICE:服務(wù)進(jìn)程
  • IMPORTANCE_BACKGROUND:后臺(tái)進(jìn)程
  • IMPORTANCE_EMPTY:空進(jìn)程
    這幾種優(yōu)先級(jí)的回收順序是 Empty process村视、Background process官套、Service process、Visible process、Foreground process奶赔。

谷歌進(jìn)程生命周期介紹

ActivityManagerService 集中管理所有進(jìn)程的內(nèi)存資源分配惋嚎。所有進(jìn)程需要申請(qǐng)或釋放內(nèi)存之前必須調(diào)用 ActivityManagerService 對(duì)象,獲得其“許可”之后才能進(jìn)行下一步操作纺阔,或者 ActivityManagerService 將直接“代勞”瘸彤。類 ActivityManagerService 中涉及到內(nèi)存回收的幾個(gè)重要的成員方法如下:trimApplications()updateOomAdjLocked()笛钝,activityIdleInternal()质况。這幾個(gè)成員方法主要負(fù)責(zé) Android 默認(rèn)的內(nèi)存回收機(jī)制,若 Linux 內(nèi)核中的內(nèi)存回收機(jī)制沒有被禁用玻靡,則跳過默認(rèn)回收结榄。

默認(rèn)回收過程

Android 操作系統(tǒng)中的內(nèi)存回收可分為兩個(gè)層次,即默認(rèn)內(nèi)存回收與內(nèi)核級(jí)內(nèi)存回收囤捻【世剩回收動(dòng)作入口:activityIdleInternal()。

Android 系統(tǒng)中內(nèi)存回收的觸發(fā)點(diǎn)大致可分為三種情況蝎土。第一视哑,用戶程序調(diào)用 StartActivity(), 使當(dāng)前活動(dòng)的 Activity 被覆蓋;第二誊涯,用戶按 back 鍵挡毅,退出當(dāng)前應(yīng)用程序;第三暴构,啟動(dòng)一個(gè)新的應(yīng)用程序跪呈。這些能夠觸發(fā)內(nèi)存回收的事件最終調(diào)用的函數(shù)接口就是 activityIdleInternal()。當(dāng) ActivityManagerService 接收到異步消息 IDLE_TIMEOUT_MSG 或者 IDLE_NOW_MSG 時(shí)取逾,activityIdleInternal() 將會(huì)被調(diào)用耗绿。

清單 1. IDLE_NOW_MSG 的處理方式
case IDLE_NOW_MSG:{ 
    IBinder token = (Ibinder)msg.obj; 
    activityIdle(token, null); 
} 
break;

清單 2. IDLE_TIMEOUT_MSG 的處理方式
case IDLE_TIMEOUT_MSG: {
   if (mDidDexOpt) { 
      mDidDexOpt = false; 
      Message nmsg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG); 
      nmsg.obj = msg.obj; 
      mHandler.sendMessageDelayed(nmsg, IDLE_TIMEOUT); 
      return; 
  } 
  IBinder token = (IBinder)msg.obj; 
  Slog.w(TAG, "Activity idle timeout for " + token); 
  activityIdleInternal(token, true, null); 
} 
break;

IDLE_NOW_MSG 由 Activity 的切換以及 Activiy 焦點(diǎn)的改變等事件引發(fā),IDLE_TIMEOUT_MSG 在 Activity 啟動(dòng)超時(shí)的情況下引發(fā)砾隅,一般這個(gè)超時(shí)時(shí)間設(shè)為 10s误阻,如果10s之內(nèi)一個(gè) Activity 依然沒有成功啟動(dòng),那么將發(fā)送異步消息 IDLE_TIMEOUT_MSG 進(jìn)行資源回收晴埂。

activityIdleInternal() 的主要任務(wù)是改變系統(tǒng)中 Activity 的狀態(tài)信息究反,并將其添加到不同狀態(tài)列表中。其主要工作如下:

  • 首先邑时,調(diào)用 scheduleAppGcsLocked() 方法通知所有進(jìn)行中的任務(wù)進(jìn)行垃圾回收。scheduleAppGcsLocked() 將進(jìn)行調(diào)度 JVM 的 garbage collect特姐,回收一部分內(nèi)存空間晶丘,這里僅僅是通知每個(gè)進(jìn)程自行進(jìn)程垃圾檢查并調(diào)度回收時(shí)間,而非同步回收。
  • 然后浅浮,取出 mStoppingActivities 和 mFinishigActivities 列表中的所有內(nèi)容沫浆,暫存在臨時(shí)變量中。這兩個(gè)列表分別存儲(chǔ)了當(dāng)前狀態(tài)為 stop 和 finish 的 activity 對(duì)象滚秩。+ 對(duì)于 stop 列表专执,如果其中的 activity 的 finish 狀態(tài)為 true,判斷是不是要立即停止郁油,如果要立即停止則調(diào)用 destroyActivityLocked() 通知目標(biāo)進(jìn)程調(diào)用 onDestroy() 方法本股,否則,先調(diào)用 resumeTopActivity() 運(yùn)行下一個(gè) Activity桐腌。如果 finish 狀態(tài)為 false拄显,則調(diào)用 stopActivityLocked() 通知客戶進(jìn)程停止該 Activity,這種情況一般發(fā)生在調(diào)用 startActivity() 后案站。對(duì)于 finish 列表躬审,直接調(diào)用 destroyActivityLocked() 通知客戶進(jìn)程銷毀目標(biāo) Activity。

這里的 destroyActivityLocked 等函數(shù)并沒有真正意義上改變內(nèi)存的使用蟆盐,只是將其狀態(tài)改變?yōu)椤霸试S回收”承边,真正的回收在下面即將調(diào)用的 trimApplications() 函數(shù)中。

回收過程函數(shù) trimApplications()

trimApplications() 函數(shù)的結(jié)構(gòu)如下 :

清單 3. trimApplications 函數(shù)
private final void trimApplications() { 
  synchronized (this) { 

    // First remove any unused application processes whose package 
    // has been removed. 

    for (i=mRemovedProcesses.size()-1; i>=0; i--) {
       (1)//kill process;
     } 

    if (!updateOomAdjLocked()) {
       (2)//do something default 
    } 

    // Finally, if there are too many activities now running, try to 
    // finish as many as we can to get back down to the limit. (3)do something 
  } 
}

清單 3 中的三個(gè)標(biāo)序號(hào)的位置分別負(fù)責(zé)如下工作:
(1)當(dāng)程序執(zhí)行到 trimApplications() 之后石挂,首先檢查 mRemovedProcesses 列表中的進(jìn)程博助。mRemovedProcesses 列表中主要包含了 crash 的進(jìn)程、5 秒內(nèi)沒有響應(yīng)并被用戶選在強(qiáng)制關(guān)閉的進(jìn)程誊稚、以及應(yīng)用開發(fā)這調(diào)用 killBackgroundProcess 想要?dú)⑺赖倪M(jìn)程翔始。調(diào)用 Process.killProcess 將所有此類進(jìn)程全部殺死。
(2)調(diào)用 updateOomAdjLocked() 函數(shù)里伯,若成功返回城瞎,說明 Linux 內(nèi)核支持 setOomAdj() 接口,updateOomAdjLocked 將修改 adj 的值并通知 linux 內(nèi)核疾瓮,內(nèi)核根據(jù) adj 值以及內(nèi)存使用情況動(dòng)態(tài)管理進(jìn)程資源(lowmemorykiller 和 oom_killer)脖镀。若 updateOomAdjLocked() 返回為假,則表示當(dāng)前系統(tǒng)不支持 setOomAdj() 接口狼电,將在本地進(jìn)行默認(rèn)的資源回收蜒灰。
(3)最后,如果當(dāng)前依然運(yùn)行了過多的 Activity肩碟,對(duì)多余的 Activity 進(jìn)行回收强窖。 trimApplications() 的大多數(shù)的代碼都在處理 Oom_killer 不存在情況下的默認(rèn)資源回收,下面對(duì)其默認(rèn)回收過程(即代碼清單中標(biāo)記(2)的位置)進(jìn)行進(jìn)一步分析削祈。其回收過程可大致描述如下翅溺。
 
步驟一脑漫,獲取當(dāng)前所有運(yùn)行的進(jìn)程 mLruProcesses,mLruProcesses 中的排序規(guī)則是按最近使用時(shí)間咙崎。對(duì) mLruProcesses 中不能被關(guān)閉的進(jìn)程進(jìn)行計(jì)數(shù)优幸,這些不能被關(guān)閉的進(jìn)程包括運(yùn)行 service 的進(jìn)程,運(yùn)行 broadcast receiver 的進(jìn)程等褪猛,見如下代碼网杆。

清單 4. 計(jì)數(shù)不能被關(guān)閉的進(jìn)程
if (app.persistent || app.services.size() != 0 || app.curReceiver != null || app.persistentActivities > 0) {
   // Don't count processes holding services against our
   // maximum process count. numServiceProcs++; 
}

步驟二, 設(shè)當(dāng)前最大運(yùn)行進(jìn)程數(shù) curMaxProcs = curMaxProcs + numServiceProcs(即默認(rèn)最大進(jìn)程數(shù)與運(yùn)行 Service 的進(jìn)程數(shù)之和)伊滋,如果當(dāng)前進(jìn)程的數(shù)量 mRemovedProcesses.size() 大于這個(gè)值碳却,則遍歷所有當(dāng)前運(yùn)行的進(jìn)程,殺死符合條件的那些進(jìn)程并釋放內(nèi)存新啼。清理過程見清單 5(部分代碼省略)追城。從清單 5 的代碼中可以看出,進(jìn)程被殺死的條件是:
必須是非 persistent 進(jìn)程燥撞,即非系統(tǒng)進(jìn)程座柱;
必須是空進(jìn)程,即進(jìn)程中沒有任何 activity 存在物舒。如果殺死存在 Activity 的進(jìn)程色洞,有可能關(guān)閉用戶正在使用的程序,或者使應(yīng)用程序恢復(fù)的時(shí)延變大冠胯,從而影響用戶體驗(yàn)火诸;
必須無 broadcast receiver。運(yùn)行 broadcast receiver 一般都在等待一個(gè)事件的發(fā)生荠察,用戶并不希望此類程序被系統(tǒng)強(qiáng)制關(guān)閉置蜀;
進(jìn)程中 service 的數(shù)量必須為 0。存在 service 的進(jìn)程很有可能在為一個(gè)或者多個(gè)程序提供某種服務(wù)悉盆,如 GPS 定位服務(wù)盯荤。殺死此類進(jìn)程將使其他進(jìn)程無法正常服務(wù)。

以上條件缺一不可焕盟。

清單 5. 清理過程
if (!app.persistent && app.activities.size() == 0 && app.curReceiver == null && app.services.size() == 0) { 
  if (app.pid > 0 && app.pid != MY_PID) { 
    Process.killProcess(app.pid); 
  } else { 
    try { 
      app.thread.scheduleExit(); 
    } catch (Exception e) { 
      // Ignore exceptions. 
    } 
  } 
  // todo: For now we assume the application is not buggy 
  // or evil, and will quit as a result of our request. 
  // Eventually we need to drive this off of the death 
  // notification, and kill the process if it takes too long.

   cleanUpApplicationRecordLocked(app, false, i);
   i--; 

  }

步驟三秋秤,再次檢查當(dāng)前運(yùn)行的進(jìn)程,如果 mRemovedProcesses.size() 仍然大于 curMaxProcs脚翘,則放寬條件再次進(jìn)行回收灼卢。判斷條件見代碼清單 6(部分代碼省略)。下面代碼中来农,布爾變量 canQuit 的值為真時(shí)鞋真,那么這個(gè)進(jìn)程可以被回收。canQuit 的取值分兩個(gè)步驟沃于,首先是根據(jù)進(jìn)程的屬性賦值涩咖。

  1. 必須是非 persistent 進(jìn)程赶袄,即非系統(tǒng)進(jìn)程;
  2. 必須無 broadcast receiver抠藕;
  3. 進(jìn)程中 service 的數(shù)量必須為 0;
  4. persistent 類型的 activity 數(shù)量為 0蒋困。

與步驟二唯一的不同在第 4 條盾似,這里不要求進(jìn)程是空進(jìn)程,只要進(jìn)程中沒有 persistent 類型的 Activity 就可以(Activity 是否是 persistent 類型在開發(fā)階段指定)雪标。這些條件都滿足時(shí)零院,再檢查進(jìn)程中每個(gè) Activity 的屬性,當(dāng)該進(jìn)程中所有的 Activity 都還必須滿足三個(gè)條件:

  • Activity 的狀態(tài)已經(jīng)保存村刨,
  • 當(dāng)前處在不可見狀態(tài)
  • 并且 Activity 已經(jīng) Stop告抄。

這時(shí)殺掉進(jìn)程只會(huì)降低下次調(diào)用程序時(shí)的加載速度,下次啟動(dòng)時(shí)將恢復(fù)到關(guān)閉之前的狀態(tài)嵌牺,并不會(huì)在用戶體驗(yàn)上造成致命的影響打洼,所以,canQuit 置位為真逆粹。這種情況與步驟二的回收方式也有所不同募疮,由于進(jìn)程中 Activity 的數(shù)量不是 0,下一步需要對(duì)每個(gè) activity 執(zhí)行 destroyActivityLocked() 銷毀僻弹,最后才殺死進(jìn)程阿浓。

清單 6. 執(zhí)行 destroyActivityLocked() 銷毀
boolean canQuit = !app.persistent && app.curReceiver == null 
&& app.services.size() == 0 && app.persistentActivities == 0;

int NUMA = app.activities.size(); 
for (j=0; j<NUMA && canQuit; j++) { 
  HistoryRecord r = (HistoryRecord)app.activities.get(j); 
  canQuit = (r.haveState || !r.stateNotNeeded) && !r.visible && r.stopped; 
} 

if (canQuit) { 

  // Finish all of the activities, and then the app itself. 
  for (j=0; j<NUMA; j++) { 
    HistoryRecord r = (HistoryRecord)app.activities.get(j);
    if (!r.finishing) { 
      destroyActivityLocked(r, false); 
    } 
    r.resultTo = null; 
  }

   if (app.pid > 0 && app.pid != MY_PID) {
     Process.killProcess(app.pid);
   }
   cleanUpApplicationRecordLocked(app, false, i);
   i--;
   //dump();
 }

步驟四,上面 3 個(gè)過程都是針對(duì)整個(gè) process 進(jìn)行的資源回收蹋绽。在以上過程執(zhí)行完畢之后芭毙,將在更小的粒度上對(duì) Activity 的資源進(jìn)行回收。與上面所述類似卸耘,列表 mLRUActivities 存儲(chǔ)了當(dāng)前所有運(yùn)行中的 Activity退敦,排序規(guī)則同樣為最少訪問原則。mLRUActivities.size() 返回系統(tǒng)中運(yùn)行的 Activity 的數(shù)量鹊奖,當(dāng)其大于 MAX_ACTIVITIES(MAX_ACTIVITIES 是一個(gè)常量苛聘,一般值為 20,代表系統(tǒng)中最大允許同時(shí)存在的 Activity)時(shí)忠聚。將回收部分滿足條件的 Activity 以減少內(nèi)存的使用设哗。回收條件代碼清單 7 所示:

清單 7. 回收條件代碼
//Finally, if there are too many activities now running, try to 
// finish as many as we can to get back down to the limit. 
for ( i=0; i<mLRUActivities.size() && mLRUActivities.size() > curMaxActivities; i++) { 
  final HistoryRecord r = (HistoryRecord)mLRUActivities.get(i); 
  // We can finish this one if we have its icicle saved and 
  // it is not persistent. 

  if ((r.haveState || !r.stateNotNeeded) && !r.visible && r.stopped && !r.persistent && !r.finishing) { 

    final int origSize = mLRUActivities.size(); 
    destroyActivityLocked(r, true); 
    if (origSize > mLRUActivities.size()) { 
      i--; 
    } 

  }

 }

這里回收的只是 Activity 的內(nèi)存資源两蟀,并不會(huì)殺死進(jìn)程网梢,也不會(huì)影響進(jìn)程的運(yùn)行。當(dāng)進(jìn)程需要調(diào)用被殺掉的 Activity 時(shí)赂毯,可以從保存的狀態(tài)中回復(fù)战虏,當(dāng)然可能需要相對(duì)長一點(diǎn)的時(shí)延拣宰。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市烦感,隨后出現(xiàn)的幾起案子巡社,更是在濱河造成了極大的恐慌,老刑警劉巖手趣,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件晌该,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡绿渣,警方通過查閱死者的電腦和手機(jī)朝群,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來中符,“玉大人姜胖,你說我怎么就攤上這事〉砩ⅲ” “怎么了右莱?”我有些...
    開封第一講書人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長档插。 經(jīng)常有香客問我隧出,道長,這世上最難降的妖魔是什么阀捅? 我笑而不...
    開封第一講書人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任胀瞪,我火速辦了婚禮,結(jié)果婚禮上饲鄙,老公的妹妹穿的比我還像新娘凄诞。我一直安慰自己,他們只是感情好忍级,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開白布帆谍。 她就那樣靜靜地躺著,像睡著了一般轴咱。 火紅的嫁衣襯著肌膚如雪汛蝙。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,760評(píng)論 1 289
  • 那天朴肺,我揣著相機(jī)與錄音窖剑,去河邊找鬼。 笑死戈稿,一個(gè)胖子當(dāng)著我的面吹牛西土,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鞍盗,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼需了,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼跳昼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起肋乍,我...
    開封第一講書人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤鹅颊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后墓造,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體挪略,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年滔岳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挽牢。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡谱煤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出禽拔,到底是詐尸還是另有隱情刘离,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布睹栖,位于F島的核電站硫惕,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏野来。R本人自食惡果不足惜恼除,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望曼氛。 院中可真熱鬧豁辉,春花似錦、人聲如沸舀患。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽聊浅。三九已至餐抢,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間低匙,已是汗流浹背旷痕。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留顽冶,地道東北人苦蒿。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像渗稍,于是被迫代替她去往敵國和親佩迟。 傳聞我的和親對(duì)象是個(gè)殘疾皇子团滥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

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