Android进程管理——进程优先级adj
1. Android应用进程的分类
Android将应用进程分为五大类,分别为Forground类、Visible类、Service类、Background类及Empty类。
- Forground类
该类中的进程重要性最高,属于该类的进程包括下面几种情况:
- 含一个前端Activity(即onResume函数被调用过了,或者说当前正在显示的那个Activity)。
- 含一个Service,并且该Service和一个前端Activity绑定(例如Music应用包括一个前端界面和一个播放Service,当我们一边听歌一边操作Music界面时,该Service即和一个前端Activity绑定)。
- 含一个调用了startForground的Service,或者该进程的Service正在调用其生命周期的函数(onCreate、onStart或onDestroy)。
- 最后一种情况是,该进程中有BroadcastReceiver实例正在执行onReceive函数。
-
Visible类
该进程中没有处于前端的组件,但是用户仍然能看到它们,例如位于一个对话框后的Activity界面。目前该类进程包括两种:
- 该进程包含一个仅onPause被调用的Activity(即它还在前台,只不过部分界面被遮住)。
- 或者包含一个Service,并且该Service和一个Visible(或Forground)的Activity绑定(从字面意义上看,这种情况不太好和Forground进程中第二种情况区分)。(3) Service类、Background类及Empty类
这三类进程都没有可见的部分,具体情况如下。
- **Service进程:**该类进程包含一个Service。此Service通过startService启动,并且不属于前面两类进程。这种进程一般在后台默默地干活,例如前面介绍的MediaScannerService。
- **Background进程:**该类进程包含当前不可见的Activity(即它们的onStop被调用过)。系统保存这些进程到一个LRU(最近最少使用)列表。当系统需要回收内存时,该列表中那些最近最少使用的进程将被杀死。
- **Empty进程:**这类进程中不包含任何组件。为什么会出现这种不包括任何组件的进程呢?其实很简单,假设该进程仅创建了一个Activity,它完成工作后主动调用finish函数销毁(destroy)自己,之后该进程就会成为Empty进程。系统保留Empty进程的原因是当又重新需要它们时(例如用户在别的进程中通过startActivity启动了它们),可以省去fork进程、创建Android运行环境等一系列漫长而艰苦的工作。
通过以上介绍可发现,当某个进程和前端显示有关系时,其重要性相对要高,这或许是体现Google重视用户体验的一个很直接的证据吧。
五个还是四个有疑问
developer.android.com/guide/compo…
2. Android 进程优先级的相关概念
2.1 oom_score_adj级别
对于每一个运行中的进程,Linux 内核都通过 proc 文件系统暴露 /proc/[pid]/oom_score_adj
这样一个文件来允许其他程序修改指定进程的优先级,这个文件允许的值的范围是:-1000 ~ +1001之间。值越小,表示进程越重要。当内存非常紧张时,系统便会遍历所有进程,以确定哪个进程需要被杀死以回收内存,此时便会读取 oom_score_adj
这个文件的值。
PS:在Linux 2.6.36之前的版本中,Linux 提供调整优先级的文件是 /proc/[pid]/oom_adj 。这个文件允许的值的范围是-17 ~ +15之间。数值越小表示进程越重要。 这个文件在新版的 Linux 中已经废弃。但你仍然可以使用这个文件,当你修改这个文件的时候,内核会直接进行换算,将结果反映到 oom_score_adj 这个文件上。Android早期版本的实现中也是依赖 oom_adj 这个文件。但是在新版本中,已经切换到使用 oom_score_adj 这个文件。
为了便于管理,ProcessList.java中预定义了 oom_score_adj
的可能取值,这里的预定义值也是对应用进程的一种分类。
// 这是一个仅托管不可见活动的进程,因此它可以在没有任何干扰的情况下被终止。
static final int CACHED_APP_MAX_ADJ = 999;
static final int CACHED_APP_MIN_ADJ = 900;
// 这是我们允许首先退出的oom_adj级别。除非正在为进程分配oom_score_adj等于CACHED_APP_MAX_ADJ,
// 否则它不能等于CACHED_APP_MAX_ADJ。
static final int CACHED_APP_LMK_FIRST_ADJ = 950;
// SERVICE_ADJ的B列表-这些是旧的和老化的服务,不如A列表中的服务那么好看。
static final int SERVICE_B_ADJ = 800;
// 这是用户之前使用过的应用程序的进程。该进程保持在其他内容之上,因**为切换回先前的应用程**序非常常见。
这对于最近的任务切换(在两个最近的应用程序之间切换)以及正常的UI流非常重要,
例如在电子邮件应用程序中点击URI以在浏览器中查看,然后按返回键返回到电子邮件。
static final int PREVIOUS_APP_ADJ = 700;
// 这是托管主页应用程序的进程-即使它通常处于后台状态,我们也要尽量避免杀死它,因为用户与之交互非常频繁。
static final int HOME_APP_ADJ = 600;
// 这是托管应用程序服务的进程-杀死它不会对用户产生太大影响。
static final int SERVICE_ADJ = 500;
// 这是具有重型应用程序的进程。它在后台运行,但我们希望尽量避免杀死它。
在启动时在system/rootdir/init.rc中设置的值。
static final int HEAVY_WEIGHT_APP_ADJ = 400;
// 这是目前正在进行备份操作的进程。杀死它并不完全致命,但通常不是一个好主意。
static final int BACKUP_APP_ADJ = 300;
// 这是由系统(或其他应用程序)绑定的进程,比服务更重要,但不会立即影响用户如果被杀死。
static final int PERCEPTIBLE_LOW_APP_ADJ = 250;
// 这是仅托管对用户可见组件的进程,并且我们确实希望避免杀死它们,但它们不是立即可见的。
// 例如,后台音乐播放就是一个例子。
static final int PERCEPTIBLE_APP_ADJ = 200;
// 这是仅托管对用户可见活动的进程,因此我们希望它们不会消失。
static final int VISIBLE_APP_ADJ = 100;
//200 -100 -1
static final int VISIBLE_APP_LAYER_MAX = PERCEPTIBLE_APP_ADJ - VISIBLE_APP_ADJ - 1;
// 这是最近处于前景应用程序并移动到FGS的进程。在一段时间内,继续将其视为几乎与前台应用程序相同。
// @see TOP_TO_FGS_GRACE_PERIOD
static final int PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ = 50;
// 运行当前前台应用程序的进程。是普通应用程序能够获取到的最高优先级。
static final int FOREGROUND_APP_ADJ = 0;
// 系统或持久进程绑定到的进程,并指出它很重要。
static final int PERSISTENT_SERVICE_ADJ = -700;
// 这是一个系统持久进程,例如电话。绝对不希望杀死它,但这样做并不完全致命。
static final int PERSISTENT_PROC_ADJ = -800;
// 系统进程以默认调整运行。
static final int SYSTEM_ADJ = -900;
// 用于原生进程的特殊代码,这些进程不受系统管理(因此系统不会分配oom adj)。
static final int NATIVE_ADJ = -1000;
一些重要的adj级别
FOREGROUND_APP_ADJ = 0
是普通应用程序能够获取到的最高优先级。
VISIBLE_APP_ADJ
是具有可见Activity进程的优先级:同一时刻,不一定只有一个Activity是可见的,如果前台Activity设置了透明属性,那么背后的Activity也是可见的。
PERCEPTIBLE_APP_ADJ
是指用户可感知的进程,可感知的进程包括:
- 进程中包含了处于pause状态或者正在pause的Activity
- 进程中包含了正在stop的Activity
- 进程中包含了前台的Service
HEAVY_WEIGHT_APP_ADJ
描述的重量级进程是指那些通过Manifest指明不能保存状态的应用进程。
除此之外,Android系统中,有一些系统应用会常驻内存,这些应用通常是系统实现的一部分,如果它们不存在,系统将处于比较奇怪的状态,例如SystemUI(状态栏,Keyguard都处于这个应用中)。
所以它们的优先级比所有应用进程的优先级更高:PERSISTENT_SERVICE_ADJ = -700
,PERSISTENT_PROC_ADJ = -800
。
另外,还有一些系统服务的实现,如果这些系统服务不存在,系统将无法工作,所以这些应用的优先级最高,几乎是任何任何时候都需要存在的:SYSTEM_ADJ = -900
,NATIVE_ADJ = -1000
。
2.2 ProcessRecord中涉及oom_score_adj的属性
**ProcessRecord.java**
//用于LRU列表控制
long lastActivityTime; // For managing the LRU list
long lruWeight; // Weight for ordering in LRU list
//和oom_adj有关
int maxAdj; // Maximum OOM adjustment for thisprocess
int hiddenAdj; // If hidden, this is the adjustment touse
int curRawAdj; // Current OOM unlimited adjustment forthis process
int setRawAdj; // Last set OOM unlimited adjustment forthis process
int curAdj; // Current OOM adjustment for thisprocess
int setAdj; // Last set OOM adjustment for thisprocess
//和调度优先级有关
int curSchedGroup; // Currently desired scheduling class
int setSchedGroup; // Last set to background scheduling class
//回收内存级别,见后文解释
int trimMemoryLevel; // Last selected memorytrimming level
//判断该进程的状态,主要和其中运行的Activity,Service有关
boolean keeping; // Actively running code sodon't kill due to that?
boolean setIsForeground; // Running foreground UI when last set?
boolean foregroundServices; // Running anyservices that are foreground?
boolean foregroundActivities; // Running anyactivities that are foreground?
boolean systemNoUi; // This is a system process, but notcurrently showing UI.
boolean hasShownUi; // Has UI been shown in this process since itwas started?
boolean pendingUiClean; // Want to clean up resources from showingUI?
boolean hasAboveClient; // Bound using BIND_ABOVE_CLIENT, so wantto be lower
//是否处于系统BadProcess列表
boolean bad; // True if disabled in the badprocess list
//描述该进程因为是否有太多后台组件而被杀死
boolean killedBackground; // True when proc has been killed due to toomany bg
String waitingToKill; // Process is waiting to be killed whenin the bg; reason
//序号,每次调节进程优先级或者LRU列表位置时,这些序号都会递增
int adjSeq; // Sequence id for identifyingoom_adj assignment cycles
int lruSeq; // Sequence id for identifyingLRU update cycles
2.2.1 ProcessState
进程的状态会影响虚拟机对于进程的内存分配和垃圾回收策略,ProcessRecord中的下面这几个属性记录了进程的状态:
对应的在 ActivityManager 重定义了 process_state 级别的划分,Android 系统会在修改进程状态的同时更新 oom_score_adj
的分级:
!images.notend.cn/2018-03-15-…
2.2.2 ScheduleGroup
内核负责了进程的CPU调度,所有运行中的进程并非能平等的能获取相等的时间片。在ProcessRecord中,通过Schedule Group来记录进程的调度组:
对应到底层进程分组,除了上面提到的 Process.java
定义的不同线程组的定义,同时还为 Activity manager
定义了一套类似的调度分组,和之前的线程分组定义也存在对应关系:
frameworks/base/services/core/java/com/android/server/am/ProcessList.java
// Activity manager's version of Process.THREAD_GROUP_BACKGROUND
static final int SCHED_GROUP_BACKGROUND = 0;
// Activity manager's version of Process.THREAD_GROUP_RESTRICTED
static final int SCHED_GROUP_RESTRICTED = 1;
// Activity manager's version of Process.THREAD_GROUP_DEFAULT
static final int SCHED_GROUP_DEFAULT = 2;
// Activity manager's version of Process.THREAD_GROUP_TOP_APP
public static final int SCHED_GROUP_TOP_APP = 3;
// Activity manager's version of Process.THREAD_GROUP_TOP_APP
// Disambiguate between actual top app and processes bound to the top app
static final int SCHED_GROUP_TOP_APP_BOUND = 4;
value | ActivityManager.java | ProcessList.java |
---|---|---|
0 | Process.THREAD_GROUP_BACKGROUND | SCHED_GROUP_BACKGROUND |
1 | Process.THREAD_GROUP_RESTRICTED | SCHED_GROUP_RESTRICTED |
2 | Process.THREAD_GROUP_DEFAULT | SCHED_GROUP_DEFAULT |
3 | Process.THREAD_GROUP_TOP_APP | SCHED_GROUP_TOP_APP |
4 | Process.THREAD_GROUP_TOP_APP | SCHED_GROUP_TOP_APP_BOUND |
3.Android 进程优先级的变化
进程的优先级反应了系统对于进程重要性的判定。
在Android系统中,进程的优先级影响着以下三个因素:
- 当内存紧张时,系统对于进程的回收策略
- 系统对于进程的CPU调度策略
- 虚拟机对于进程的内存分配和垃圾回收策略
我们知道影响 Android 应用进程优先级变化的是根据 Android应用组件的生命周期变化相关。Android进程调度之adj算法 里面罗列了所有会触发进程状态发生变化的事件,主要包括:
组件 | 方法名称 | 描述 |
---|---|---|
Activity | realStartActivityLocked | 启动Activity |
resumeTopActivityInnerLocked | 恢复栈顶Activity | |
finishCurrentActivityLocked | 结束当前Activity | |
destroyActivityLocked | 摧毁当前Activity | |
Service | realStartServiceLocked | 启动服务 |
bindServiceLocked | 绑定服务(只更新当前app) | |
unbindServiceLocked | 解绑服务(只更新当前app) | |
bringDownServiceLocked | 结束服务(只更新当前app) | |
sendServiceArgsLocked | 在启动或清理服务过程中调用(只更新当前app) | |
Broadcast | BQ.processNextBroadcast | 处理下一个广播 |
BQ.processCurBroadcastLocked | 处理当前广播 | |
BQ.deliverToRegisteredReceiverLocked | 分发已注册的广播(只更新当前app) | |
ContentProvider | AMS.removeContentProvider | 移除provider |
AMS.publishContentProviders | 发布provider(只更新当前app) | |
AMS.getContentProviderImpl | 获取provider(只更新当前app) | |
Process | setSystemProcess | 创建并设置系统进程 |
addAppLocked | 创建persistent进程 | |
attachApplicationLocked | 进程创建后attach到system_server的过程 | |
trimApplications | 清除未使用的app | |
appDiedLocked | 进程死亡 | |
killAllBackgroundProcesses | 杀死所有后台进程(ADJ>9或removed=true的普通进程) | |
killPackageProcessesLocked | 以包名的形式杀掉相关进程 |
以上都会直接或间接调用到 AMS 中的 updateOomAdjLocked
方法来更新进程的优先级,最终实现在OomAdjuster.java类中。
3.1 OomAdjuster的updateOomAdjLocked
主要有以下两个方法负责更新进程优先级
void updateOomAdjLocked(String oomAdjReason) 针对所有进程
boolean updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll, String oomAdjReason) {针对单个进程
3.1.1 针对单独进程调整adj updateOomAdjLocked
对应代码
private final boolean updateOomAdjLocked(ProcessRecord app, int cachedAdj,
ProcessRecord TOP_APP, boolean doingAll, long now) {
if (app.thread == null) {
return false;
}
computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now, false);
return applyOomAdjLocked(app, doingAll, now, SystemClock.elapsedRealtime());
}
3.1.2 computeOomAdjLocked
computeOomAdjLocked
方法负责计算进程的优先级,总计约700行,实在是太长了。简短总结为根据进程的不同状态分别更新adj,procstate和schedgruop。总结为下图。没有挨个校验,具体以实际代码为准。
3.1.3 applyOomAdjLocked
- 据adj变化处理内存压缩 AppCompactor
- 更新进程的oom adj,通过socket写给lmkd
- 更新SchedGroup
- 对于需要被杀的bg进程杀掉。如果waitingToKill不为null(waitingToKill =reason),receiver为空并且为SCHED_GROUP_BACKGROUND
- 根据 ProcessList.java ActivityManager.java两者group对应关系最终通过setProcessGroup(pid, group);更新group,联系上边看下。
- 对于变成前台或者从前台变化的进程,进行RT和FIFO的变换,通过scheduleAsFifoPriority和setThreadPriority更改优先级和实时策略
- 更新nextPssTime,这个值即是下次更新PSS的时间。
- 和PROCESS_STATE_SERVICE相比较,判断是不是重要进程
- 新单个进程的memFactor状态,这个值和trim memory相关
/** Applies the computed oomadj, procstate and sched group values and freezes them in set* */
@GuardedBy("mService")
private final boolean applyOomAdjLocked(ProcessRecord app, boolean doingAll, long now,
long nowElapsed) {
boolean success = true;
if (app.getCurRawAdj() != app.setRawAdj) {
app.setRawAdj = app.getCurRawAdj();
}
int changes = 0;
// don't compact during bootup
//1.内存压缩相关逻辑
if (mAppCompact.useCompaction() && mService.mBooted) {
// Cached and prev/home compaction
if (app.curAdj != app.setAdj) {
// Perform a minor compaction when a perceptible app becomes the prev/home app
// Perform a major compaction when any app enters cached
// reminder: here, setAdj is previous state, curAdj is upcoming state
if (app.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ &&
(app.curAdj == ProcessList.PREVIOUS_APP_ADJ ||
app.curAdj == ProcessList.HOME_APP_ADJ)) {
mAppCompact.compactAppSome(app);
} else if ((app.setAdj < ProcessList.CACHED_APP_MIN_ADJ
|| app.setAdj > ProcessList.CACHED_APP_MAX_ADJ)
&& app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ
&& app.curAdj <= ProcessList.CACHED_APP_MAX_ADJ) {
mAppCompact.compactAppFull(app);
}
} else if (mService.mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE
&& app.setAdj < ProcessList.FOREGROUND_APP_ADJ
// Because these can fire independent of oom_adj/procstate changes, we need
// to throttle the actual dispatch of these requests in addition to the
// processing of the requests. As a result, there is throttling both here
// and in AppCompactor.
&& mAppCompact.shouldCompactPersistent(app, now)) {
mAppCompact.compactAppPersistent(app);
} else if (mService.mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE
&& app.getCurProcState()
== ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
&& mAppCompact.shouldCompactBFGS(app, now)) {
mAppCompact.compactAppBfgs(app);
}
}
//2.设置进程的oomadj
if (app.curAdj != app.setAdj) {
ProcessList.setOomAdj(app.pid, app.uid, app.curAdj);
if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.info.uid) {
String msg = "Set " + app.pid + " " + app.processName + " adj "
+ app.curAdj + ": " + app.adjType;
reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
}
app.setAdj = app.curAdj;
app.verifiedAdj = ProcessList.INVALID_ADJ;
}
final int curSchedGroup = app.getCurrentSchedulingGroup();
//3.更新进程SchedGroup,判断进程是否需要被杀
if (app.setSchedGroup != curSchedGroup) {
int oldSchedGroup = app.setSchedGroup;
app.setSchedGroup = curSchedGroup;
if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.uid) {
String msg = "Setting sched group of " + app.processName
+ " to " + curSchedGroup + ": " + app.adjType;
reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
}
//如果waitingToKill不为null(waitingToKill =reason),receiver为空并且为SCHED_GROUP_BACKGROUND
则被杀
if (app.waitingToKill != null && app.curReceivers.isEmpty()
&& app.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND) {
app.kill(app.waitingToKill, true);
success = false;
} else {
int processGroup;
switch (curSchedGroup) {
case ProcessList.SCHED_GROUP_BACKGROUND:
processGroup = THREAD_GROUP_BG_NONINTERACTIVE;
break;
case ProcessList.SCHED_GROUP_TOP_APP:
case ProcessList.SCHED_GROUP_TOP_APP_BOUND:
processGroup = THREAD_GROUP_TOP_APP;
break;
case ProcessList.SCHED_GROUP_RESTRICTED:
processGroup = THREAD_GROUP_RESTRICTED;
break;
default:
processGroup = THREAD_GROUP_DEFAULT;
break;
}
mProcessGroupHandler.sendMessage(mProcessGroupHandler.obtainMessage(
0 /* unused */, app.pid, processGroup));
//4.对于变成前台或者从前台变化的进程,进行RT和FIFO的变换,通过scheduleAsFifoPriority和setThreadPriority
更改优先级和实时策略
try {
if (curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
// do nothing if we already switched to RT
if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
app.getWindowProcessController().onTopProcChanged();
if (mService.mUseFifoUiScheduling) {
// Switch UI pipeline for app to SCHED_FIFO
app.savedPriority = Process.getThreadPriority(app.pid);
mService.scheduleAsFifoPriority(app.pid, /* suppressLogs */true);
if (app.renderThreadTid != 0) {
mService.scheduleAsFifoPriority(app.renderThreadTid,
/* suppressLogs */true);
if (DEBUG_OOM_ADJ) {
Slog.d("UI_FIFO", "Set RenderThread (TID " +
app.renderThreadTid + ") to FIFO");
}
} else {
if (DEBUG_OOM_ADJ) {
Slog.d("UI_FIFO", "Not setting RenderThread TID");
}
}
} else {
// Boost priority for top app UI and render threads
setThreadPriority(app.pid, TOP_APP_PRIORITY_BOOST);
if (app.renderThreadTid != 0) {
try {
setThreadPriority(app.renderThreadTid,
TOP_APP_PRIORITY_BOOST);
} catch (IllegalArgumentException e) {
// thread died, ignore
}
}
}
}
} else if (oldSchedGroup == ProcessList.SCHED_GROUP_TOP_APP &&
curSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
app.getWindowProcessController().onTopProcChanged();
if (mService.mUseFifoUiScheduling) {
try {
// Reset UI pipeline to SCHED_OTHER
setThreadScheduler(app.pid, SCHED_OTHER, 0);
setThreadPriority(app.pid, app.savedPriority);
if (app.renderThreadTid != 0) {
setThreadScheduler(app.renderThreadTid,
SCHED_OTHER, 0);
setThreadPriority(app.renderThreadTid, -4);
}
} catch (IllegalArgumentException e) {
Slog.w(TAG,
"Failed to set scheduling policy, thread does not exist:\n"
+ e);
} catch (SecurityException e) {
Slog.w(TAG, "Failed to set scheduling policy, not allowed:\n" + e);
}
} else {
// Reset priority for top app UI and render threads
setThreadPriority(app.pid, 0);
if (app.renderThreadTid != 0) {
setThreadPriority(app.renderThreadTid, 0);
}
}
}
} catch (Exception e) {
if (DEBUG_ALL) {
Slog.w(TAG, "Failed setting thread priority of " + app.pid, e);
}
}
}
}
if (app.repForegroundActivities != app.hasForegroundActivities()) {
app.repForegroundActivities = app.hasForegroundActivities();
changes |= ActivityManagerService.ProcessChangeItem.CHANGE_ACTIVITIES;
}
if (app.getReportedProcState() != app.getCurProcState()) {
app.setReportedProcState(app.getCurProcState());
if (app.thread != null) {
try {
if (false) {
//RuntimeException h = new RuntimeException("here");
Slog.i(TAG, "Sending new process state " + app.getReportedProcState()
+ " to " + app /*, h*/);
}
app.thread.setProcessState(app.getReportedProcState());
} catch (RemoteException e) {
}
}
}
//5.如果进程setProcState等于PROCESS_STATE_NONEXISTENT,或者和内存记录的ProcState对比有变化
更新nextPssTime
if (app.setProcState == PROCESS_STATE_NONEXISTENT
|| ProcessList.procStatesDifferForMem(app.getCurProcState(), app.setProcState)) {
if (false && mService.mTestPssMode
&& app.setProcState >= 0 && app.lastStateTime <= (now-200)) {
// Experimental code to more aggressively collect pss while
// running test... the problem is that this tends to collect
// the data right when a process is transitioning between process
// states, which will tend to give noisy data.
long start = SystemClock.uptimeMillis();
long startTime = SystemClock.currentThreadTimeMillis();
long pss = Debug.getPss(app.pid, mTmpLong, null);
long endTime = SystemClock.currentThreadTimeMillis();
mService.recordPssSampleLocked(app, app.getCurProcState(), pss,
mTmpLong[0], mTmpLong[1], mTmpLong[2],
ProcessStats.ADD_PSS_INTERNAL_SINGLE, endTime-startTime, now);
mService.mPendingPssProcesses.remove(app);
Slog.i(TAG, "Recorded pss for " + app + " state " + app.setProcState
+ " to " + app.getCurProcState() + ": "
+ (SystemClock.uptimeMillis()-start) + "ms");
}
app.lastStateTime = now;
app.nextPssTime = ProcessList.computeNextPssTime(app.getCurProcState(),
app.procStateMemTracker, mService.mTestPssMode,
mService.mAtmInternal.isSleeping(), now);
if (DEBUG_PSS) Slog.d(TAG_PSS, "Process state change from "
+ ProcessList.makeProcStateString(app.setProcState) + " to "
+ ProcessList.makeProcStateString(app.getCurProcState()) + " next pss in "
+ (app.nextPssTime-now) + ": " + app);
} else {
if (now > app.nextPssTime || (now > (app.lastPssTime+ProcessList.PSS_MAX_INTERVAL)
&& now > (app.lastStateTime+ProcessList.minTimeFromStateChange(
mService.mTestPssMode)))) {
if (mService.requestPssLocked(app, app.setProcState)) {
app.nextPssTime = ProcessList.computeNextPssTime(app.getCurProcState(),
app.procStateMemTracker, mService.mTestPssMode,
mService.mAtmInternal.isSleeping(), now);
}
} else if (false && DEBUG_PSS) {
Slog.d(TAG_PSS,
"Not requesting pss of " + app + ": next=" + (app.nextPssTime-now));
}
}
if (app.setProcState != app.getCurProcState()) {
if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.uid) {
String msg = "Proc state change of " + app.processName
+ " to " + ProcessList.makeProcStateString(app.getCurProcState())
+ " (" + app.getCurProcState() + ")" + ": " + app.adjType;
reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
}
//6.判断是否是重要进程
boolean setImportant = app.setProcState < PROCESS_STATE_SERVICE;
boolean curImportant = app.getCurProcState() < PROCESS_STATE_SERVICE;
if (setImportant && !curImportant) {
// This app is no longer something we consider important enough to allow to use
// arbitrary amounts of battery power. Note its current CPU time to later know to
// kill it if it is not behaving well.
app.setWhenUnimportant(now);
app.lastCpuTime = 0;
}
// Inform UsageStats of important process state change
// Must be called before updating setProcState
maybeUpdateUsageStatsLocked(app, nowElapsed);
maybeUpdateLastTopTime(app, now);
app.setProcState = app.getCurProcState();
if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) {
app.notCachedSinceIdle = false;
}
//7.更新单个进程的memFactor状态,这个值和trim memory相关
if (!doingAll) {
mService.setProcessTrackerStateLocked(app,
mService.mProcessStats.getMemFactorLocked(), now);
} else {
app.procStateChanged = true;
}
} else if (app.reportedInteraction && (nowElapsed - app.getInteractionEventTime())
> mConstants.USAGE_STATS_INTERACTION_INTERVAL) {
// For apps that sit around for a long time in the interactive state, we need
// to report this at least once a day so they don't go idle.
maybeUpdateUsageStatsLocked(app, nowElapsed);
} else if (!app.reportedInteraction && (nowElapsed - app.getFgInteractionTime())
> mConstants.SERVICE_USAGE_INTERACTION_TIME) {
// For foreground services that sit around for a long time but are not interacted with.
maybeUpdateUsageStatsLocked(app, nowElapsed);
}
if (changes != 0) {
if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
"Changes in " + app + ": " + changes);
ActivityManagerService.ProcessChangeItem item =
mService.enqueueProcessChangeItemLocked(app.pid, app.info.uid);
item.changes = changes;
item.foregroundActivities = app.repForegroundActivities;
if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
"Item " + Integer.toHexString(System.identityHashCode(item))
+ " " + app.toShortString() + ": changes=" + item.changes
+ " foreground=" + item.foregroundActivities
+ " type=" + app.adjType + " source=" + app.adjSource
+ " target=" + app.adjTarget);
}
return success;
}
3.2 updateOomAdjLocked(String oomAdjReason) 针对所有进程
oomAdjReason相关的场景参考下面的表格。
常量名 | log日志 | 描述 |
---|---|---|
OOM_ADJ_REASON_NONE | updateOomAdj_meh | 没有特定的 OOM Adj 原因 |
OOM_ADJ_REASON_ACTIVITY | updateOomAdj_activityChange | Activity 变化导致的 OOM Adj 更新 |
OOM_ADJ_REASON_FINISH_RECEIVER | updateOomAdj_finishReceiver | 结束 Receiver 导致的 OOM Adj 更新 |
OOM_ADJ_REASON_START_RECEIVER | updateOomAdj_startReceiver | 启动 Receiver 导致的 OOM Adj 更新 |
OOM_ADJ_REASON_BIND_SERVICE | updateOomAdj_bindService | 绑定 Service 导致的 OOM Adj 更新 |
OOM_ADJ_REASON_UNBIND_SERVICE | updateOomAdj_unbindService | 解绑 Service 导致的 OOM Adj 更新 |
OOM_ADJ_REASON_START_SERVICE | updateOomAdj_startService | 启动 Service 导致的 OOM Adj 更新 |
OOM_ADJ_REASON_GET_PROVIDER | updateOomAdj_getProvider | 获取 Content Provider 导致的 OOM Adj 更新 |
OOM_ADJ_REASON_REMOVE_PROVIDER | updateOomAdj_removeProvider | 移除 Content Provider 导致的 OOM Adj 更新 |
OOM_ADJ_REASON_UI_VISIBILITY | updateOomAdj_uiVisibility | UI 可见性变化导致的 OOM Adj 更新 |
OOM_ADJ_REASON_WHITELIST | updateOomAdj_whitelistChange | 白名单变化导致的 OOM Adj 更新 |
OOM_ADJ_REASON_PROCESS_BEGIN | updateOomAdj_processBegin | 进程启动导致的 OOM Adj 更新 |
OOM_ADJ_REASON_PROCESS_END | updateOomAdj_processEnd | 进程结束导致的 OOM Adj 更新 |
代码太长了贴不动了。之后在分析吧
4.Process中对线程和进程的调度
frameworks/base/core/java/android/os/Process.java
/*
设置线程的Group,实际上就是设置线程的调度策略,目前Android定义了三种Group。
THREAD_GROUP_DEFAULT:Default thread group - gets a 'normal'share of the CPU
THREAD_GROUP_BG_NONINTERACTIVE:Background non-interactive thread group.
Allthreads in this group are scheduled with a reduced share of the CPU
THREAD_GROUP_FG_BOOST:Foreground 'boost' thread group - Allthreads in
this group are scheduled with an increasedshare of the CPU.
目前代码中还没有地方使用THREAD_GROUP_FG_BOOST这种Group
*/
public static final native void setThreadGroup(inttid, int group)
throws IllegalArgumentException, SecurityException;
//设置进程的调度策略,包括该进程的所有线程
public static final native void setProcessGroup(int pid, int group)
throws IllegalArgumentException, SecurityException;
设置线程的调度策略和优先级 针对tid。linux实现
public static final native void setThreadScheduler(int tid, int policy, int priority)
throws IllegalArgumentException;
//设置线程的调度优先级
public static final native void setThreadPriority(int priority)
throws IllegalArgumentException, SecurityException;
值得一提的是linux不区分进程和线程的概念。Android 中的线程对应到 Linux 内核可以看为轻量级进程,所以 Linux 为其分配资源适用 Linux 进程调度策略。其中主线程等同于应用进程的优先级。
我们为应用中各子线程设置的优先级,将直接影响到主线程在抢占各种系统资源尤其是 CPU 资源时候的优先级,所以为了保证主线程执行的顺畅,我们应尽量控制子线程的优先级。
在Android 中常见的几种异步方式 new Thread()、AysncTask、HandlerThread、ThreadPoolExecutor、IntentService。除了 AysncTask 以外,其他的创建线程的过程中,默认都是和当前线程(一般是 UI 线程)保持一样的优先级,只有 AysncTask 默认是 THREAD_PRIORITY_BACKGROUND
的优先级,所以为了保证主线程能够拥有较为优先的执行级别,建议在创建异步线程的过程中注意对优先级的控制。
5.总结
在 Android 应用状态发生变化以后,会导致进程的 oom_score_adj
、procState
、schedGroup
等进程状态的重新计算和设置,,并且通过 applyOomAdjLocked
方法将对应的优先级、adj、进程状态等值应用到进程上,从而改变进程的优先级和调度策略。以上的过程大概可以概括为:
-
通过设置进程组,改变了进程所在 cgroup,
-
通过设置调度策略实现主线程在实时优先级和普通优先级的切换,
-
通过设置优先级改变进程 nice 值,同时在底层会改变进程所在的 cgroup。
延伸问题
- 什么是cgroup、线程调度策略的区别,线程优先级的应用?
- adj更新和lmkd是如何配合?