Android 作業系統中的記憶體回收策略,android回收
Android 作業系統中的記憶體回收可分為兩個層次:1、預設記憶體回收、即Application Framework 層的預設回收。2、核心級記憶體回收。 Linux 核心中的記憶體回收 lowmemorykiller、OOM_killer。
預設記憶體回收:(代碼可查閱 ActivityManagerService.java類)回收動作入口activityIdleInternal()。
Android 系統中記憶體回收的觸發點大致可分為三種情況。第一,使用者程式調用 StartActivity(), 使當前活動的 Activity 被覆蓋;第二,使用者按 back 鍵,退出當前應用程式;第三,啟動一個新的應用程式。這些能夠觸發記憶體回收的事件最終調用的函數介面就是 activityIdleInternal()。當 ActivityManagerService 接收到非同步訊息 IDLE_TIMEOUT_MSG 或者 IDLE_NOW_MSG 時,activityIdleInternal() 將會被調用。
IDLE_NOW_MSG 由 Activity 的切換以及 Activiy 焦點的改變等事件引發,IDLE_TIMEOUT_MSG 在 Activity 啟動逾時的情況下引發,一般這個逾時時間設為 10s,如果 10s 之內一個 Activity 依然沒有成功啟動,那麼將發送非同步訊息 IDLE_TIMEOUT_MSG 進行資源回收。activityIdleInternal() 的主要任務是改變系統中 Activity 的狀態資訊,並將其添加到不同狀態列表中。它的主要工如下:
首先,調用 scheduleAppGcsLocked() 方法通知所有進行中的任務進行記憶體回收。scheduleAppGcsLocked() 將進行調度 JVM 的 garbage collect,回收一部分記憶體空間,這裡僅僅是通知每個進程自行進程垃圾檢查並調度回收時間,而非同步回收。然後,取出 mStoppingActivities 和 mFinishigActivities 列表中的所有內容,暫存在臨時變數中。這兩個列表分別儲存了目前狀態為 stop 和 finishi 的 activity 對象。對於 stop 列表,如果其中的 activity 的 finish 狀態為 true,判斷是不是要立即停止,如果要立即停止則調用 destroyActivityLocked() 通知目標進程調用 onDestroy() 方法,否則,先調用 resumeTopActivity() 運行下一個 Activity。如果 finish 狀態為 false,則調用 stopActivityLocked() 通知客戶進程停止該 Activity,這種情況一般發生在調用 startActivity() 後。對於 finish 列表,直接調用 destroyActivityLocked() 通知客戶進程銷毀目標 Activity。
這裡的 destroyActivityLocked 等函數並沒有真正意義上改變記憶體的使用,只是將其狀態改變為“允許回收”,真正的回收在下面即將調用的 trimApplications() 函數中。
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 } }
(1)當程式執行到 trimApplications() 之後,首先檢查 mRemovedProcesses 列表中的進程。mRemovedProcesses 列表中主要包含了 crash 的進程、5 秒內沒有響應並被使用者選在強制關閉的進程、以及應用開發這調用 killBackgroundProcess 想要殺死的進程。調用 Process.killProcess 將所有此類進程全部殺死。
(2)調用 updateOomAdjLocked() 函數,若成功返回,說明 Linux 核心支援 setOomAdj() 介面,updateOomAdjLocked 將修改 adj 的值並通知 linux 核心,核心根據 adj 值以及記憶體使用量情況動態管理進程資源(lowmemorykiller 和 oom_killer)。若 updateOomAdjLocked() 返回為假,則表示當前系統不支援 setOomAdj() 介面,將在本地進行預設的資源回收。
(3)最後,如果當前依然運行了過多的 Activity,對多餘的 Activity 進行回收。 trimApplications() 的大多數的代碼都在處理 Oom_killer 不存在情況下的預設資源回收,下面對其預設回收過程(即代碼中標記(2)的位置)進行進一步分析。其回收過程可大致描述如下。
步驟一,擷取當前所有啟動並執行進程 mLruProcesses,mLruProcesses 中的定序是按最近使用時間。對 mLruProcesses 中不能被關閉的進程進行計數,這些不能被關閉的進程包括運行 service 的進程,運行 broadcast receiver 的進程等
步驟二, 設當前最大運行進程數 curMaxProcs = curMaxProcs + numServiceProcs(即預設最大進程數與運行 Service 的進程數之和),如果當前進程的數量 mRemovedProcesses.size() 大於這個值,則遍曆所有當前啟動並執行進程,殺死合格那些進程並釋放記憶體。進程被殺死的條件是:必須是非 persistent 進程,即非系統進程,必須是空進程,即進程中沒有任何 activity 存在。如果殺死存在 Activity 的進程,有可能關閉使用者正在使用的程式,或者使應用程式恢複的時延變大,從而影響使用者體驗;必須無 broadcast receiver。運行 broadcast receiver 一般都在等待一個事件的發生,使用者並不希望此類程式被系統強制關閉;進程中 service 的數量必須為 0。存在 service 的進程很有可能在為一個或者多個程式提供某種服務,如 GPS 定位服務。殺死此類進程將使其他進程無法正常服務。
步驟三,再次檢查當前啟動並執行進程,如果 mRemovedProcesses.size() 仍然大於 curMaxProcs,則放寬條件再次進行回收。
步驟四,上面 3 個過程都是針對整個 process 進行的資源回收。在以上過程執行完畢之後,將在更小的粒度上對 Activity 的資源進行回收。與上面所述類似,列表 mLRUActivities 儲存了當前所有運行中的 Activity,定序同樣為最少訪問原則。mLRUActivities.size() 返回系統中啟動並執行 Activity 的數量,當其大於 MAX_ACTIVITIES(MAX_ACTIVITIES 是一個常量,一般值為 20,代表系統中最大允許同時存在的 Activity)時。將回收部分滿足條件的 Activity 以減少記憶體的使用。
這裡回收的只是 Activity 的記憶體資源,並不會殺死進程,也不會影響進程的運行。當進程需要調用被殺掉的 Activity 時,可以從儲存的狀態中回複,當然可能需要相對長一點的時延。
Linux 核心中的記憶體回收
lowmemorykiller
trimApplications() 函數中會執行一個叫做 updateOomAdjLocked() 的函數,如果返回 false,則執行預設回收,若返回 true 則不執行預設記憶體回收。updateOomAdjLocked 將針對每一個進程更新一個名為 adj 的變數,並將其告知 Linux 核心,核心維護一個包含 adj 的資料結構(即進程表),並通過 lowmemorykiller 檢查系統記憶體的使用方式,在記憶體不足的情況下殺死一些進程並釋放記憶體。
由於 Android 作業系統中的所有應用程式都運行在獨立的 Dalvik 虛擬機器環境中,Linux 核心無法獲知每個進程的運行狀態,也就無法為每個進程維護一個合適的 adj 值,因此,Android Application Framework 中必須提供一套機制以動態更新每個進程的 adj。這就是 updateOomAdjLocked()。
詳細資料可參閱:這裡寫連結內容