優先度調整
1. Androidアプリの処理の分類
Android では、アプリケーション プロセスを、Forground、Visible、Service、Background、Empty の 5 つのカテゴリに分類しています。
- フォアグラウンドクラス
このカテゴリのプロセスは最も重要であり、このカテゴリに属するプロセスには次の状況が含まれます。
- フロントエンド アクティビティ (つまり、onResume 関数が呼び出されたもの、または現在表示されているアクティビティ) が含まれます。
- サービスが含まれており、サービスはフロントエンド アクティビティにバインドされています (たとえば、音楽アプリケーションにはフロントエンド インターフェイスと再生サービスが含まれており、曲を聴きながら音楽インターフェイスを操作すると、サービスはフロントエンド アクティビティにバインドされます)フロントエンドアクティビティ)。
- startForground を呼び出した Service が含まれているか、プロセスの Service がそのライフ サイクルの関数 (onCreate、onStart、または onDestroy) を呼び出しています。
- 最後のケースは、BroadcastReceiver インスタンスがプロセス内で onReceive 関数を実行している場合です。
-
可視クラス
プロセスにはフロントエンド コンポーネントはありませんが、ダイアログ ボックスの背後にあるアクティビティ インターフェイスなどのフロントエンド コンポーネントがユーザーに表示されます。現在、次の 2 種類のプロセスがあります。
- プロセスには、onPause のみが呼び出されるアクティビティが含まれています (つまり、まだフォアグラウンドにありますが、インターフェイスの一部がカバーされています)。
- または、サービスが含まれており、そのサービスが Visible (または Forground) アクティビティにバインドされています (文字通り、この状況は、Forground プロセスの 2 番目の状況と区別するのがあまり適切ではありません)。(3) Serviceクラス、Backgroundクラス、Emptyクラス
以下に示すように、3 種類のプロセスのいずれも目に見える部分はありません。
- **サービス プロセス:** このタイプのプロセスにはサービスが含まれます。このサービスは startService によって開始され、前の 2 種類のプロセスには属しません。前に紹介した MediaScannerService など、この種のプロセスは通常、バックグラウンドで静かに動作します。
- **バックグラウンド プロセス:** このタイプのプロセスには、現在表示されていない (つまり、onStop が呼び出された) アクティビティが含まれています。システムは、これらのプロセスを LRU (最も最近使用されていない) リストに保持します。システムがメモリを再利用する必要がある場合、このリストにある最も最近使用されていないプロセスが強制終了されます。
- **空のプロセス:** このタイプのプロセスにはコンポーネントが含まれません。コンポーネントがまったく含まれていないプロセスが存在するのはなぜですか? 実際、これは非常に単純で、プロセスがアクティビティを 1 つだけ作成し、その作業が完了した後、終了関数を積極的に呼び出してそれ自体を破棄 (破棄) すると、プロセスは空のプロセスになります。システムが Empty プロセスを保持する理由は、Empty プロセスが再度必要になったとき (たとえば、ユーザーが別のプロセスの startActivity を通じて開始した場合)、fork プロセスや Android オペレーティング環境の作成などの一連の長くて大変な作業を節約できるためです。 。
上記の紹介を通じて、特定のプロセスがフロントエンド ディスプレイに関連する場合、その重要性が比較的高いことがわかります。これは、Google がユーザー エクスペリエンスを重視していることの非常に直接的な証拠である可能性があります。
5つか4つ疑わしい
2. Android プロセスの優先順位の関連概念
2.1 oom_score_adj レベル
実行中のプロセスごとに、Linux カーネルは/proc/[pid]/oom_score_adj
proc ファイル システムを通じてそのようなファイルを公開し、他のプログラムが指定されたプロセスの優先順位を変更できるようにします。このファイルの許容値の範囲は -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;
いくつかの重要な調整レベル
FOREGROUND_APP_ADJ = 0
これは、通常のアプリケーションが取得できる最高の優先順位です。
VISIBLE_APP_ADJ
これは、表示されるアクティビティ プロセスの優先順位です。同時に、必ずしも 1 つのアクティビティだけが表示されるわけではありません。前景のアクティビティに透明なプロパティ セットがある場合、その背後にあるアクティビティも表示されます。
PERCEPTIBLE_APP_ADJ
これは、ユーザーが知覚できるプロセスを指し、知覚可能なプロセスには次のものが含まれます。
- プロセスには一時停止状態または一時停止中のアクティビティが含まれています
- プロセスには停止中のアクティビティが含まれています
- プロセスにはフォアグラウンド サービスが含まれています
HEAVY_WEIGHT_APP_ADJ
説明されている重量プロセスは、マニフェストを通じて状態を保存できないアプリケーション プロセスを指します。
さらに、Android システムでは、一部のシステム アプリケーションがメモリに常駐します。これらのアプリケーションは、通常、システム実装の一部です。これらのアプリケーションが存在しない場合、システム UI (ステータス バー、キーガードなど) が異常な状態になります。このアプリケーション内にあります)。
したがって、これらはすべてのアプリケーション プロセスよりも高い優先度を持ちます: 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プロセス状態
プロセスの状態は、プロセスの仮想マシンのメモリ割り当てとガベージ コレクション戦略に影響します。ProcessRecord の次の属性は、プロセスの状態を記録します。
対応して、process_state レベルの区分が ActivityManager で再定義され、Android システムはoom_score_adj
プロセス状態の分類を変更しながら更新します。
[外部リンク画像の転送に失敗しました。ソース サイトには盗難防止リンク メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-QmcyHg5L-1688460965268)(https://link.juejin.cn/?target) =http%3A%2F% 2Fimages.notend.cn%2F2018-03-15-18-03-57.png “http://images.notend.cn/2018-03-15-18-03-57.png” ”)]
2.2.2 スケジュールグループ
カーネルはプロセスの CPU スケジューリングを担当しますが、実行中のすべてのプロセスが同じタイム スライスを等しく取得できるわけではありません。ProcessRecord では、プロセスのスケジュール グループがスケジュール グループを通じて記録されます。
基礎となるプロセス グループ化に対応して、上で定義した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;
価値 | アクティビティマネージャー.java | プロセスリスト.java |
---|---|---|
0 | プロセス.THREAD_GROUP_BACKGROUND | SCHED_GROUP_BACKGROUND |
1 | プロセス.THREAD_GROUP_RESTRICTED | SCHED_GROUP_RESTRICTED |
2 | プロセス.THREAD_GROUP_DEFAULT | SCHED_GROUP_DEFAULT |
3 | プロセス.THREAD_GROUP_TOP_APP | SCHED_GROUP_TOP_APP |
4 | プロセス.THREAD_GROUP_TOP_APP | SCHED_GROUP_TOP_APP_BOUND |
3. Androidプロセスの優先順位変更
プロセスの優先度は、プロセスの重要性に関するシステムの判断を反映します。
Android システムでは、プロセスの優先度は次の 3 つの要素に影響します。
- メモリが不足している場合、プロセスに対するシステムの回復戦略
- プロセスに対するシステムの CPU スケジューリング ポリシー
- プロセスに対する仮想マシンのメモリ割り当てとガベージ コレクション戦略
Android アプリケーション プロセスの優先順位の変更は、Android アプリケーション コンポーネントのライフ サイクルの変更に関連していることがわかっています。Android プロセス スケジューリングの adj アルゴリズムには、プロセス状態の変更をトリガーするすべてのイベントがリストされます。主に次のようなものがあります。
コンポーネント | メソッド名 | 説明 |
---|---|---|
アクティビティ | realStartActivityLocked | アクティビティの開始 |
再開トップアクティビティインナーロック済み | スタックの最上位のアクティビティを復元します | |
終了現在のアクティビティがロックされました | 現在のアクティビティを終了する | |
destroyアクティビティロック済み | 現在のアクティビティを破棄します | |
サービス | realStartServiceLocked | サービスを開始する |
バインドサービスロック済み | バインドサービス (現在のアプリのみを更新) | |
unbindServiceLocked | サービスのバインドを解除します (現在のアプリのみを更新します) | |
BringDownServiceロック済み | サービス終了(現在のアプリのみ更新) | |
sendServiceArgsLocked | 起動中またはクリーニング サービス中に呼び出されます (現在のアプリの更新のみ) | |
ブロードキャスト | BQ.processNextBroadcast | 次のブロードキャストを処理する |
BQ.processCurBroadcastLocked | 現在のブロードキャストを処理する | |
BQ.deliverToRegisteredReceiverLocked | 登録したブロードキャストを配信(現在のアプリのみ更新) | |
コンテンツプロバイダー | AMS.removeContentProvider | プロバイダーを削除する |
AMS.publishContentProviders | プロバイダーを解放します (現在のアプリのみを更新します) | |
AMS.getContentProviderImpl | プロバイダーを取得します (現在のアプリのみを更新します) | |
プロセス | setシステムプロセス | システムプロセスの作成と設定 |
addAppLocked | 永続的なプロセスを作成する | |
アタッチアプリケーションロック済み | プロセス作成後にsystem_serverにアタッチするプロセス | |
トリムアプリケーション | 使用していないアプリを消去する | |
アプリ死亡ロック済み | プロセスが終了しました | |
すべてのバックグラウンドプロセスを殺す | すべてのバックグラウンドプロセスを強制終了します(ADJ>9またはremoved=trueの通常のプロセス) | |
killPackageProcessesLocked | パッケージ名の形式で関連プロセスを強制終了します。 |
上記は、AMS のメソッドを直接または間接的に呼び出してupdateOomAdjLocked
プロセスの優先度を更新し、最終的に OomAdjuster.java クラスでそれを実現します。
3.1 オームアジャスターupdateOomAdjLocked
プロセスの優先度を更新するには主に次の 2 つの方法があります。
すべてのプロセスの 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 OomAdjLocked を計算する
computeOomAdjLocked
このメソッドはプロセスの優先度を計算する役割を果たしますが、合計で約 700 行あり、長すぎます。簡単にまとめると、プロセスのさまざまな状態に応じて adj、procstate、および schedgruop を更新するということです。それを以下の図にまとめます。一つ一つの検証は行われず、実際のコードが優先されます。
3.1.3 applyOomAdjLocked
- adj の変更に応じたプロセス メモリ圧縮 AppCompactor
- プロセスの oom adj を更新し、ソケット経由で lmkd に書き込みます。
- スケジュールされたグループを更新する
- 強制終了する必要がある bg プロセスを強制終了します。waitingToKill が null でない場合 (waitingToKill =reason)、受信側は空であり、SCHED_GROUP_BACKGROUND
- ProcessList.java ActivityManager.java の 2 つのグループの対応関係に従って、最後に setProcessGroup(pid, group); を渡してグループを更新し、上側に問い合わせて確認します。
- フォアグラウンドになるプロセス、またはフォアグラウンドから変更されるプロセスに対して、RT および FIFO 変換を実行し、scheduleAsFifoPriority および setThreadPriority を通じて優先度およびリアルタイム戦略を変更します。
- nextPssTime を更新します。この値は次回 PSS を更新する時間です。
- PROCESS_STATE_SERVICEと比較して重要なプロセスかどうかを判断
- 新しい単一プロセスの memFactor 状態。この値はメモリのトリミングに関連します。
/** 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 関連のシナリオについては、以下の表を参照してください。
定数名 | ログログ | 説明 |
---|---|---|
OOM_ADJ_REASON_NONE | updateOomAdj_meh | 特定の OOM 調整原因なし |
OOM_ADJ_REASON_ACTIVITY | updateOomAdj_activityChange | アクティビティ変更による OOM 調整更新 |
OOM_ADJ_REASON_FINISH_RECEIVER | updateOomAdj_finishReceiver | Receiver による OOM Adj 更新を終了する |
OOM_ADJ_REASON_START_RECEIVER | updateOomAdj_startReceiver | Receiverの起動によるOOM調整アップデート |
OOM_ADJ_REASON_BIND_SERVICE | updateOomAdj_bindService | サービスのバインディングによって発生する OOM 調整更新 |
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是如何配合?
最后
如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。
如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。
全套视频资料:
一、面试合集
二、源码解析合集
3. オープンソース フレームワークのコレクションは、
ワン クリックと 3 つのリンクで誰でもサポートできるようになっています。記事内の情報が必要な場合は、記事の最後にある CSDN 公式認定 WeChat カードをクリックして無料で入手してください↓↓↓