1. 程式人生 > >Android 作業系統中的記憶體回收

Android 作業系統中的記憶體回收

原文:https://blog.csdn.net/anlike/article/details/77749833 

Android 系統中記憶體回收的觸發點大致可分為三種情況:

第一種情況:使用者程式呼叫 StartActivity(), 使當前活動的 Activity 被覆蓋

第二種情況:按下Back鍵,會呼叫finishActivityLocked,然後把Activity的finishing標誌設為true,然後再呼叫startPausingLocked,當目標actiity完成暫停後,就會通知Ams,此時Ams從completePaused開始執行,由於此時暫停的Activity的finising狀態已經變為true,所以會執行finishingActivtyLocked。

第三種情況:啟動一個新的應用程式。向Ams傳送一個Idle訊息,這會導致Ams開始執行activityIdleInternal方法,該方法首先處理mStoppingActivities中的物件,接著處理mFinishingActivities列表,然後再呼叫trimApplications
這些能夠觸發記憶體回收的事件最終呼叫的函式介面就是 activityIdleInternalLocked().

1.當 ActivityManagerService 接收到非同步訊息 DLE_NOW_MSG 時 將會被呼叫。
2.當 ActivityManagerService 接收到非同步訊息 IDLE_TIMEOUT_MSG 時將會被呼叫。
3.ActivityManagerService.的activityIdle中將會被呼叫
一,
1.IDLE_NOW_MSG 由 Activity 的切換以及 Activiy 焦點的改變等事件引發.
2.IDLE_TIMEOUT_MSG 在 Activity 啟動超時的情況下引發,一般這個超時時間設為 10s,如果 10s 之內一個 Activity 依然沒有成功啟動,那麼將傳送非同步訊息 IDLE_TIMEOUT_MSG 進行資源回收。
檔案路徑:
Z:\HLOS\frameworks\base\services\core\java\com\android\server\am\ActivityStackSupervisor.java

private final class ActivityStackSupervisorHandler extends Handler {
。。。。。。。。。
void activityIdleInternal(ActivityRecord r) {
synchronized (mService) {
activityIdleInternalLocked(r != null ? r.appToken : null, true, null);
}
}

@Override
public void handleMessage(Message msg) {
switch (msg.what) {
.......
case IDLE_TIMEOUT_MSG: {
if (DEBUG_IDLE) Slog.d(TAG_IDLE,
"handleMessage: IDLE_TIMEOUT_MSG: r=" + msg.obj);
if (mService.mDidDexOpt) {
mService.mDidDexOpt = false;
Message nmsg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG);
nmsg.obj = msg.obj;
mHandler.sendMessageDelayed(nmsg, IDLE_TIMEOUT);
return;
}
// We don't at this point know if the activity is fullscreen,
// so we need to be conservative and assume it isn't.
activityIdleInternal((ActivityRecord)msg.obj);
} break;
case IDLE_NOW_MSG: {
if (DEBUG_IDLE) Slog.d(TAG_IDLE, "handleMessage: IDLE_NOW_MSG: r=" + msg.obj);
activityIdleInternal((ActivityRecord)msg.obj);
} break;
}
}


3.activityIdle是在handleResumeActivity新增一個空閒任務,然後在looper執行緒空閒的時候呼叫
Z:\HLOS\frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java

@Override
public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
final long origId = Binder.clearCallingIdentity();
synchronized (this) {
ActivityStack stack = ActivityRecord.getStackLocked(token);
if (stack != null) {
ActivityRecord r =
mStackSupervisor.activityIdleInternalLocked(token, false, config);
if (stopProfiling) {
if ((mProfileProc == r.app) && (mProfileFd != null)) {
try {
mProfileFd.close();
} catch (IOException e) {
}
clearProfilerLocked();
}
}
}
}
Binder.restoreCallingIdentity(origId);
}
主要就是檢測當前activity棧是否為空,如果棧中有activity,那麼就呼叫ActivityStactSupervisor.activityIdleInternalLocked方法

Z:\HLOS\frameworks\base\core\java\android\app\ActivityThread.java
呼叫ActivityManagerService的activityIdle方法在ActivityThread的內部handler Idler中。
private class Idler implements MessageQueue.IdleHandler {
@Override
public final boolean queueIdle() {
ActivityClientRecord a = mNewActivities;
boolean stopProfiling = false;
if (mBoundApplication != null && mProfiler.profileFd != null
&& mProfiler.autoStopProfiler) {
stopProfiling = true;
}
if (a != null) {
mNewActivities = null;
IActivityManager am = ActivityManagerNative.getDefault();
ActivityClientRecord prev;
do {
if (localLOGV) Slog.v(
TAG, "Reporting idle of " + a +
" finished=" +
(a.activity != null && a.activity.mFinished));
if (a.activity != null && !a.activity.mFinished) {
try {
am.activityIdle(a.token, a.createdConfig, stopProfiling);
a.createdConfig = null;
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
prev = a;
a = a.nextIdle;
prev.nextIdle = null;
} while (a != null);
}
if (stopProfiling) {
mProfiler.stopProfiling();
}
ensureJitEnabled();
return false;
}
}

這是Message佇列的內部類閒置handler,這個queueIdle回撥在訊息佇列中沒有訊息可以處理的空閒時期被調起,此前已經分析過了。
queueIdle()
return true,表示保留,當queueIdle執行完畢之後,不會移除這個IdleHandler
return false,表示這個IdleHandler不需要保留,也就是隻需要執行一遍。

接著應該看Looper.myQueue().addIdleHandler()什麼時候把Idler 新增進去的了

final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
ActivityClientRecord r = mActivities.get(token);
..............

if (!r.onlyLocalRequest) {
r.nextIdle = mNewActivities;
mNewActivities = r;
if (localLOGV) Slog.v(
TAG, "Scheduling idle handler for " + r);
Looper.myQueue().addIdleHandler(new Idler());
}
r.onlyLocalRequest = false;

// Tell the activity manager we have resumed.
if (reallyResume) {
try {
ActivityManagerNative.getDefault().activityResumed(token);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}

} else {
// If an exception was thrown when trying to resume, then
// just end this activity.
try {
ActivityManagerNative.getDefault()
.finishActivity(token, Activity.RESULT_CANCELED, null,
Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}

也就是說,在activity執行resume方法之後,系統會在當前的執行緒中新增一個空閒任務。
這邊需要科普一下,一般我們在呼叫了finish方法,或者是啟動了一個新的應用或者是activity方法之後,當前的activity會處於後臺,並且處於空閒,因此就會觸發queueIdle的方法,從而觸發AMS的activityIdle的方法。

二,
現在我們可以主要分析activityIdleInternalLocked方法了
Z:\HLOS\frameworks\base\services\core\java\com\android\server\am\ActivityStackSupervisor.java

final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
Configuration config) {
if (DEBUG_ALL) Slog.v(TAG, "Activity idle: " + token);

ArrayList<ActivityRecord> finishes = null;
ArrayList<UserState> startingUsers = null;
int NS = 0;
int NF = 0;
boolean booting = false;
boolean activityRemoved = false;

ActivityRecord r = ActivityRecord.forTokenLocked(token);
if (r != null) {
if (DEBUG_IDLE) Slog.d(TAG_IDLE, "activityIdleInternalLocked: Callers="
+ Debug.getCallers(4));
mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
r.finishLaunchTickingLocked();
if (fromTimeout) {
reportActivityLaunchedLocked(fromTimeout, r, -1, -1);
}

// This is a hack to semi-deal with a race condition
// in the client where it can be constructed with a
// newer configuration from when we asked it to launch.
// We'll update with whatever configuration it now says
// it used to launch.
if (config != null) {
r.configuration = config;
}

// We are now idle. If someone is waiting for a thumbnail from
// us, we can now deliver.
r.idle = true;

//Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout);
if (isFocusedStack(r.task.stack) || fromTimeout) {
booting = checkFinishBootingLocked();
}
}
//1.通知所有需要記憶體回收的程序進行記憶體回收(這些程序都儲存在mProgressToGc列表中)
if (allResumedActivitiesIdle()) {
if (r != null) {
mService.scheduleAppGcsLocked();
}

if (mLaunchingActivity.isHeld()) {
mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
if (VALIDATE_WAKE_LOCK_CALLER &&
Binder.getCallingUid() != Process.myUid()) {
throw new IllegalStateException("Calling must be system uid");
}
mLaunchingActivity.release();
}
ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
}
2. 分別拿到所有要stop和finish的activity存放在stops和finishs容器中,然後將記錄清空
// Atomically retrieve all of the other things to do.
// 獲取已經暫停的activity列表
final ArrayList<ActivityRecord> stops = processStoppingActivitiesLocked(true);
NS = stops != null ? stops.size() : 0;
if ((NF = mFinishingActivities.size()) > 0) {
// 獲取已經觸發了finish方法的列表
finishes = new ArrayList<>(mFinishingActivities);
mFinishingActivities.clear();
}

if (mStartingUsers.size() > 0) {
startingUsers = new ArrayList<>(mStartingUsers);
mStartingUsers.clear();
}

// Stop any activities that are scheduled to do so but have been
// waiting for the next one to start.
for (int i = 0; i < NS; i++) {
r = stops.get(i);
final ActivityStack stack = r.task.stack;
if (stack != null) {
//如果該被暫停的activity已經呼叫了finish方法,那麼就呼叫棧的finish當前的activity的方法
if (r.finishing) {
stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false);
} else {
// 否則呼叫棧的stopActivity方法
stack.stopActivityLocked(r);
}
}
}

// Finish any activities that are scheduled to do so but have been
// waiting for the next one to start.
// 遍歷finish列表中的每一個activity,如果當前棧不為空,就去觸發棧的destroyActivityLocked方法
for (int i = 0; i < NF; i++) {
r = finishes.get(i);
final ActivityStack stack = r.task.stack;
if (stack != null) {
activityRemoved |= stack.destroyActivityLocked(r, true, "finish-idle");
//並沒有真正意義上改變記憶體的使用,只是將其狀態改變為“允許回收”,真正的回收在下面即將呼叫的 trimApplications() 函式中
}
}

if (!booting) {
// Complete user switch
if (startingUsers != null) {
for (int i = 0; i < startingUsers.size(); i++) {
mService.mUserController.finishUserSwitch(startingUsers.get(i));
}
}
}

mService.trimApplications();//真正開始殺程序回收
//dump();
//mWindowManager.dump();

if (activityRemoved) {
resumeFocusedStackTopActivityLocked(); //刪除成功後恢復焦點堆疊activity的顯示
}

return r;
}

一、scheduleAppGcsLocked()分析//通知各程序要GC
Z:\HLOS\frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java

/**
* Schedule the execution of all pending app GCs.
*/
final void scheduleAppGcsLocked() {
mHandler.removeMessages(GC_BACKGROUND_PROCESSES_MSG);
//從mProcessesToGc列表中取出下一個ProcessRecord ,併發送一個延遲訊息,由performAppGcsIfAppropriateLocked()來執行
if (mProcessesToGc.size() > 0) {
// Schedule a GC for the time to the next process.
ProcessRecord proc = mProcessesToGc.get(0);
Message msg = mHandler.obtainMessage(GC_BACKGROUND_PROCESSES_MSG);

long when = proc.lastRequestedGc + GC_MIN_INTERVAL;
long now = SystemClock.uptimeMillis();
if (when < (now+GC_TIMEOUT)) {
when = now + GC_TIMEOUT;
}
mHandler.sendMessageAtTime(msg, when);
}
}
final class MainHandler extends Handler {
......
case GC_BACKGROUND_PROCESSES_MSG: {
synchronized (ActivityManagerService.this) {
performAppGcsIfAppropriateLocked();
}
} break;
......
}

/**
* If all looks good, perform GCs on all processes waiting for them.
*/
final void performAppGcsIfAppropriateLocked() {
if (canGcNowLocked()) {
performAppGcsLocked();
return;
}
// Still not idle, wait some more.
scheduleAppGcsLocked();
}

/**
* Returns true if things are idle enough to perform GCs.
*/
//判斷是否足夠閒置可以來執行GC
private final boolean canGcNowLocked() {
boolean processingBroadcasts = false;
for (BroadcastQueue q : mBroadcastQueues) {
if (q.mParallelBroadcasts.size() != 0 || q.mOrderedBroadcasts.size() != 0) {
processingBroadcasts = true;
}
}
return !processingBroadcasts
&& (isSleepingLocked() || mStackSupervisor.allResumedActivitiesIdle());
}

Z:\HLOS\frameworks\base\services\core\java\com\android\server\am\ActivityStackSupervisor.java
boolean allResumedActivitiesIdle() {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
if (!isFocusedStack(stack) || stack.numActivities() == 0) {
continue;
}
final ActivityRecord resumedActivity = stack.mResumedActivity;
if (resumedActivity == null || !resumedActivity.idle) {
if (DEBUG_STATES) Slog.d(TAG_STATES, "allResumedActivitiesIdle: stack="
+ stack.mStackId + " " + resumedActivity + " not idle");
return false;
}
}
}
// Send launch end powerhint when idle
mService.mActivityStarter.sendPowerHintForLaunchEndIfNeeded();
return true;
}
/**
* Perform GCs on all processes that are waiting for it, but only
* if things are idle.
*/
final void performAppGcsLocked() {
final int N = mProcessesToGc.size();
if (N <= 0) {
return;
}
if (canGcNowLocked()) {
while (mProcessesToGc.size() > 0) {
ProcessRecord proc = mProcessesToGc.remove(0); //在mProcessesToGc列表中逐個取出每個需要進行gc的ProcessRecord物件,同時移除
if (proc.curRawAdj > ProcessList.PERCEPTIBLE_APP_ADJ || proc.reportLowMemory) {
if ((proc.lastRequestedGc+GC_MIN_INTERVAL)
<= SystemClock.uptimeMillis()) {

// To avoid spamming the system, we will GC processes one
// at a time, waiting a few seconds between each.
//如果超過了最小時間間隔,則從mProcessesToGc列表中取出下一個app,併發送一個延遲訊息
performAppGcLocked(proc);
scheduleAppGcsLocked();
return;
} else {
// It hasn't been long enough since we last GCed this
// process... put it in the list to wait for its time.
//先檢查app上一次進行gc的時間,並和當前時間進行對比,如果還沒超過最小間隔,則將指定的app 加入到mProcessesToGc列表中
addProcessToGcListLocked(proc);
break;
}
}
}

scheduleAppGcsLocked();
}
}
/**
* Ask a given process to GC right now.
*/
final void performAppGcLocked(ProcessRecord app) {
try {
app.lastRequestedGc = SystemClock.uptimeMillis();
if (app.thread != null) {
if (app.reportLowMemory) {
app.reportLowMemory = false;
app.thread.scheduleLowMemory();
} else {
app.thread.processInBackground();
}
}
} catch (Exception e) {
// whatever.
}
}

Z:\HLOS\frameworks\base\core\java\android\app\ActivityThread.java
@Override
public void scheduleLowMemory() {
sendMessage(H.LOW_MEMORY, null);
}
public void processInBackground() {
mH.removeMessages(H.GC_WHEN_IDLE);
mH.sendMessage(mH.obtainMessage(H.GC_WHEN_IDLE));
}
private class H extends Handler {
......
case LOW_MEMORY:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "lowMemory");
handleLowMemory();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case GC_WHEN_IDLE:
scheduleGcIdler();
break;
.......
}

final void handleLowMemory() {
ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(true, null);

final int N = callbacks.size();
for (int i=0; i<N; i++) {
callbacks.get(i).onLowMemory(); //回撥該程序所包含的所有元件的onLowMemory方法
}
// Ask SQLite to free up as much memory as it can, mostly from its page caches.
if (Process.myUid() != Process.SYSTEM_UID) {
int sqliteReleased = SQLiteDatabase.releaseMemory(); //不是SYSTEM_UID就釋放SQLite模組佔用的記憶體。
EventLog.writeEvent(SQLITE_MEM_RELEASED_EVENT_LOG_TAG, sqliteReleased);
}
// Ask graphics to free up as much as possible (font/image caches)
Canvas.freeCaches(); //釋放應用中所有的canvas物件
// Ask text layout engine to free also as much as possible
Canvas.freeTextLayoutCaches();//釋放應用中所有的Text Layout
BinderInternal.forceGc("mem"); //釋放該程序的Binder物件
}

void scheduleGcIdler() {
if (!mGcIdlerScheduled) {
mGcIdlerScheduled = true;
Looper.myQueue().addIdleHandler(mGcIdler);//正常GC都是任務空閒時執行
}
mH.removeMessages(H.GC_WHEN_IDLE);
}
final class GcIdler implements MessageQueue.IdleHandler {
@Override
public final boolean queueIdle() {
doGcIfNeeded();
return false;
}
}
MIN_TIME_BETWEEN_GCS = 5*1000;
void doGcIfNeeded() {
mGcIdlerScheduled = false;
final long now = SystemClock.uptimeMillis();
//Slog.i(TAG, "**** WE MIGHT WANT TO GC: then=" + Binder.getLastGcTime()
// + "m now=" + now);
if ((BinderInternal.getLastGcTime()+MIN_TIME_BETWEEN_GCS) < now) {
//兩次GC間隔不能小於5S
//Slog.i(TAG, "**** WE DO, WE DO WANT TO GC!");
BinderInternal.forceGc("bg"); //釋放該程序的Binder物件
}
}

Z:\HLOS\frameworks\base\core\java\com\android\internal\os\BinderInternal.java
public static void forceGc(String reason) {
EventLog.writeEvent(2741, reason);
VMRuntime.getRuntime().requestConcurrentGC(); //虛擬機器GC
}

二、trimApplications()分析 //真正回收記憶體
final void trimApplications() {
synchronized (this) {
int i;

// First remove any unused application processes whose package
// has been removed.
for (i=mRemovedProcesses.size()-1; i>=0; i--) {
final ProcessRecord app = mRemovedProcesses.get(i);
//1.必須是空程序,即程序中沒有任何 activity 存在。如果殺死存在 Activity 的程序,有可能關閉使用者正在使用的程式,或者使應用程式恢復的時延變大,從而影響使用者體驗;
//2.必須無 broadcast receiver。執行 broadcast receiver 一般都在等待一個事件的發生,使用者並不希望此類程式被系統強制關閉;
//3.程序中 service 的數量必須為 0。存在 service 的程序很有可能在為一個或者多個程式提供某種服務,如 GPS 定位服務。殺死此類程序將使其他程序無法正常服務
if (app.activities.size() == 0
&& app.curReceiver == null && app.services.size() == 0) {
Slog.i(
TAG, "Exiting empty application process "
+ app.toShortString() + " ("
+ (app.thread != null ? app.thread.asBinder() : null)
+ ")\n");
if (app.pid > 0 && app.pid != MY_PID) {
app.kill("empty", false);
} else {
try {
app.thread.scheduleExit();//pid為0說明已經殺過了,pid==MY_PID說明是當前system程序
} catch (Exception e) {
// Ignore exceptions.
}
}
cleanUpApplicationRecordLocked(app, false, true, -1, false /*replacingPid*/);
mRemovedProcesses.remove(i);

if (app.persistent) {
addAppLocked(app.info, false, null /* ABI override */);
}
}
}

// Now update the oom adj for all processes.
updateOomAdjLocked();
}
}
Z:\HLOS\frameworks\base\core\java\android\app\ActivityThread.java
public final void scheduleExit() {
sendMessage(H.EXIT_APPLICATION, null);
}
private class H extends Handler {
......
case EXIT_APPLICATION:
if (mInitialApplication != null) {
mInitialApplication.onTerminate(); //This method is for use in emulated process environments
}
Looper.myLooper().quit(); //退出
break;
......
}
Z:\HLOS\frameworks\base\services\core\java\com\android\server\am\ProcessRecord.java

void kill(String reason, boolean noisy) {
if (!killedByAm) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "kill");
if (noisy) {
Slog.i(TAG, "Killing " + toShortString() + " (adj " + setAdj + "): " + reason);
}
if(toShortString().contains("com.android.dialer")||toShortString().contains("com.android.server.telecom")){
Slog.i(TAG, "Killing return leihujun dialer");
return;
}
EventLog.writeEvent(EventLogTags.AM_KILL, userId, pid, processName, setAdj, reason);
Process.killProcessQuiet(pid);
ActivityManagerService.killProcessGroup(uid, pid);
if (!persistent) {
killed = true;
killedByAm = true;
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}


1.mRemovedProcesses 列表中主要包含了 crash 的程序、5 秒內沒有響應並被使用者選在強制關閉的程序、以及應用開發這呼叫 killBackgroundProcess 想要殺死的程序。呼叫 Process.killProcess 將所有此類程序全部殺死
---------------------