一、初步了解AMS的进程管理

引用邓凡平的深入理解系列的一些知识

1.  Linux进程调度优先级和调度策略

调度优先级和调度策略是操作系统中一个很重要的概念。简而言之,它是系统中CPU资源的管理和控制手段。如何理解?此处进行简单介绍。读者可自行阅读操作系统方面的书籍以加深理解。

·  相对于在OS上运行的应用进程个数来说,CPU的资源非常有限。

·  调度优先级是OS分配CPU资源给应用进程时(即调度应用进程运行)需要参考的一个指标。一般而言,优先级高的进程将更有机会得到CPU资源。

调度策略用于描述OS调度模块分配CPU给应用进程所遵循的规则,即当将CPU控制权交给调度模块时,系统如何选择下一个要运行的进程。此处不能仅考虑各进程的调度优先级,因为存在多个进程具有相同调度优先级的情况。在这种情况下,一个需要考虑的重要因素是,每个进程所分配的时间片及它们的使用情况.

提示可简单认为,调度优先级及调度策略二者一起影响了系统分配CPU资源给应用进程。注意,此处的措词为“影响”,而非“控制”。因为对于用户空间可以调用的API来说,如果二者能控制CPU资源分配,那么该系统的安全性会大打折扣。

Linux提供了两个API用于设置调度优先级及调度策略。先来看设置调度优先级的函数setpriority,其原型如下:

int setpriority(int which, int who, int prio);

其中:

·  which和who参数联合使用。当which为PRIO_PROGRESS时,who代表一个进程;当which为PRIO_PGROUP时,who代表一个进程组;当which为PRIO_USER时,who代表一个uid。

·  第三个参数prio用于设置应用进程的nicer值,可取范围从-20到19。Linux kernel用nicer值来描述进程的调度优先级,该值越大,表明该进程越友(nice),其被调度运行的几率越低。

下面来看设置调度策略的函数sched_setscheduler,其原型如下:

int sched_setscheduler(pid_t pid, int policy,conststruct sched_param *param);

其中:

·  第一个参数为进程id。

·  第二个参数为调度策略。目前Android支持三种调度策略:SCHED_OTHER,标准round-robin分时共享策略(也就是默认的策略);SCHED_BATCH,针对具有batch风格(批处理)进程的调度策略;SCHED_IDLE,针对优先级非常低的适合在后台运行的进程。除此之外,Linux还支持实时(Real-time)调度策略,包括SCHED_FIFO,先入先出调度策略;SCHED_RR,:round-robin调度策略,也就是循环调度。这些策略的定义值在Process.java,下面的文章有介绍这个类

·  param参数中最重要的是该结构体中的sched_priority变量。针对Android中的三种非实时调度策略,该值必须为NULL。

以上介绍了调度优先级和调度策略的概念。建议读者做个小实验来测试调动优先级及调动策略的作用,步骤如下:

·  挂载SD卡到PC机并往向其中复制一些媒体文件,然后重新挂载SD卡到手机。该操作就能触发MediaScanner扫描新增的这些媒体文件。

·  利用top命令查看CPU使用率,会发现进程com.process.media(即MediaScanner所在的进程)占用CPU较高(可达70%以上),原因是该进程会扫描媒体文件,该工作将利用较多的CPU资源。

·  此时,如果启动另一个进程,然后做一些操作,会发现MediaScanner所在进程的CPU利用率会降下来(例如下降到30%~40%),这表明系统将CPU资源更多地分给了用户正在操作的这个进程。

出现这种现象的原因是,MediaScannerSerivce的扫描线程将调度优先级设置为11,而默认的调度优先级为0。 相比而言,MediaScannerService优先级真的很高。

2.  关于Linux进程oom_adj的介绍

从Linux kernel 2.6.11开始,内核提供了进程的OOM控制机制,目的是当系统出现内存不足(out of memory,简称OOM)的情况时,Kernel可根据进程的oom_adj来选择并杀死一些进程,以回收内存。简而言之,oom_adj可标示Linux进程内存资源的优先级,其可取范围从-16到15,另外有一个特殊值-17用于禁止系统在OOM情况下杀死该进程。和nicer值一样,oom_adj的值越高,那么在OOM情况下,该进程越有可能被杀掉。每个进程的oom_adj初值为0。

Linux没有提供单独的API用于设置进程的oom_adj。目前的做法就是向/proc/进程id/oom_adj文件中写入对应的oom_adj值,通过这种方式就能调节进程的oom_adj了。

另外,有必要简单介绍一下Android为Linux Kernel新增的lowmemorykiller(以后简称LMK)模块的工作方式:LMK的职责是根据当前内存大小去杀死对应oom_adj及以上的进程以回收内存。这里有两个关键参数:为LMK设置不同的内存阈值及oom_adj,它们分别由/sys/module/lowmemorykiller/parameters/minfree和/sys/module/lowmemorykiller/parameters/adj控制。

注意这两个参数的典型设置为:

minfree,2048,3072,4096,6144,7168,8192 用于描述不同级别的内存阈值,单位为KB。

adj,0,1,2,4,7,15 用于描述对应内存阈值的oom_adj值。

表示当剩余内存为2048KB时,LMK将杀死oom_adj大于等于0的进程。

网络上有一些关于Android手机内存优化的方法,其中一种就利用了LMK的工作原理。

提示lowmemorykiller的代码在kernel/drivers/staging/android/lowmemorykiller.c中,感兴趣的读者可尝试自行阅读。

3.关于Android中的进程管理的介绍

前面介绍了Linux OS中进程管理(包括调度和OOM控制)方面的API,但AMS是如何利用它们的呢?这就涉及AMS中的进程管理规则了。这里简单介绍相关规则。

Android将应用进程分为五大类,分别为Forground类、Visible类、Service类、Background类及Empty类。这五大类的划分各有规则。

1.  进程分类

(1) Forground类

该类中的进程重要性最高,属于该类的进程包括下面几种情况:

·  含一个前端Activity(即onResume函数被调用过了,或者说当前正在显示的那个Activity)。

·  含一个Service,并且该Service和一个前端Activity绑定(例如Music应用包括一个前端界面和一个播放Service,当我们一边听歌一边操作Music界面时,该Service即和一个前端Activity绑定)。

·  含一个调用了startForground的Service,或者该进程的Service正在调用其生命周期的函数(onCreate、onStart或onDestroy)。

·  最后一种情况是,该进程中有BroadcastReceiver实例正在执行onReceive函数。

(2) Visible类

属于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重视用户体验的一个很直接的证据吧。

建议读者可阅读SDK/docs/guide/topics/fundamentals/processes-and-threads.html以获取更为详细的信息。

二、AMS的进程管理详情

AMS的进程管理跟三个类密切关系ProcessList,ProcessRecord和Process。方法的话有

updateLruProcessLocked、computeOomAdjLocked、updateOomAdjLocked、applyOomAdjLocked 。其实在AMS中保持所有的进程信息

final ArrayList<ProcessRecord> mLruProcesses = new ArrayList<ProcessRecord>();//所有的进程信息

final ArrayList<ProcessRecord> mRemovedProcesses = new ArrayList<ProcessRecord>();//移除的进程信息

final SparseArray<ProcessRecord> mPidsSelfLocked = new SparseArray<ProcessRecord>();用pid来查询相关的进程信息

2.1.1 ProcessList

public final class ProcessList {private static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessList" : TAG_AM;// The minimum time we allow between crashes, for us to consider this// application to be bad and stop and its services and reject broadcasts.static final int MIN_CRASH_INTERVAL = 60*1000;// OOM adjustments for processes in various states:// Uninitialized value for any major or minor adj fieldsstatic final int INVALID_ADJ = -10000;// Adjustment used in certain places where we don't know it yet.// (Generally this is something that is going to be cached, but we// don't know the exact value in the cached range to assign yet.)static final int UNKNOWN_ADJ = 1001;// This is a process only hosting activities that are not visible,// so it can be killed without any disruption.static final int CACHED_APP_MAX_ADJ = 906;static final int CACHED_APP_MIN_ADJ = 900;// The B list of SERVICE_ADJ -- these are the old and decrepit// services that aren't as shiny and interesting as the ones in the A list.static final int SERVICE_B_ADJ = 800;// This is the process of the previous application that the user was in.// This process is kept above other things, because it is very common to// switch back to the previous app.  This is important both for recent// task switch (toggling between the two top recent apps) as well as normal// UI flow such as clicking on a URI in the e-mail app to view in the browser,// and then pressing back to return to e-mail.static final int PREVIOUS_APP_ADJ = 700;// This is a process holding the home application -- we want to try// avoiding killing it, even if it would normally be in the background,// because the user interacts with it so much.static final int HOME_APP_ADJ = 600;// This is a process holding an application service -- killing it will not// have much of an impact as far as the user is concerned.static final int SERVICE_ADJ = 500;// This is a process with a heavy-weight application.  It is in the// background, but we want to try to avoid killing it.  Value set in// system/rootdir/init.rc on startup.static final int HEAVY_WEIGHT_APP_ADJ = 400;// This is a process currently hosting a backup operation.  Killing it// is not entirely fatal but is generally a bad idea.static final int BACKUP_APP_ADJ = 300;// This is a process only hosting components that are perceptible to the// user, and we really want to avoid killing them, but they are not// immediately visible. An example is background music playback.static final int PERCEPTIBLE_APP_ADJ = 200;// This is a process only hosting activities that are visible to the// user, so we'd prefer they don't disappear.static final int VISIBLE_APP_ADJ = 100;static final int VISIBLE_APP_LAYER_MAX = PERCEPTIBLE_APP_ADJ - VISIBLE_APP_ADJ - 1;// This is the process running the current foreground app.  We'd really// rather not kill it!static final int FOREGROUND_APP_ADJ = 0;// This is a process that the system or a persistent process has bound to,// and indicated it is important.static final int PERSISTENT_SERVICE_ADJ = -700;// This is a system persistent process, such as telephony.  Definitely// don't want to kill it, but doing so is not completely fatal.static final int PERSISTENT_PROC_ADJ = -800;// The system process runs at the default adjustment.static final int SYSTEM_ADJ = -900;// Special code for native processes that are not being managed by the system (so// don't have an oom adj assigned by the system).static final int NATIVE_ADJ = -1000;// Memory pages are 4K.static final int PAGE_SIZE = 4*1024;private final int[] mOomAdj = new int[] {FOREGROUND_APP_ADJ, VISIBLE_APP_ADJ, PERCEPTIBLE_APP_ADJ,BACKUP_APP_ADJ, CACHED_APP_MIN_ADJ, CACHED_APP_MAX_ADJ};// These are the low-end OOM level limits.  This is appropriate for an// HVGA or smaller phone with less than 512MB.  Values are in KB.private final int[] mOomMinFreeLow = new int[] {12288, 18432, 24576,36864, 43008, 49152};// These are the high-end OOM level limits.  This is appropriate for a// 1280x800 or larger screen with around 1GB RAM.  Values are in KB.private final int[] mOomMinFreeHigh = new int[] {73728, 92160, 110592,129024, 147456, 184320};

这个类主要是用于OOM的设定值。什么时候会发生OOM呢?跟oom_adj 值有很大关系。oom_adj 的值有几个档位如下

ADJ级别    取值  含义
NATIVE_ADJ  -1000   native进程
SYSTEM_ADJ  -900    仅指system_server进程
PERSISTENT_PROC_ADJ -800    系统persistent进程
PERSISTENT_SERVICE_ADJ  -700    关联着系统或persistent进程
FOREGROUND_APP_ADJ  0   前台进程
VISIBLE_APP_ADJ 100 可见进程
PERCEPTIBLE_APP_ADJ 200 可感知进程,比如后台音乐播放
BACKUP_APP_ADJ  300 备份进程
HEAVY_WEIGHT_APP_ADJ    400 重量级进程
SERVICE_ADJ 500 服务进程
HOME_APP_ADJ    600 Home进程
PREVIOUS_APP_ADJ    700 上一个进程
SERVICE_B_ADJ   800 B List中的Service
CACHED_APP_MIN_ADJ  900 不可见进程的adj最小值
CACHED_APP_MAX_ADJ  906 不可见进程的adj最大值
UNKNOWN_ADJ = 1001   进程初始化都是会赋予这个值。

为了防止剩余内存过低,Android在内核空间有lowmemorykiller(简称LMK),LMK是通过注册shrinker来触发低内存回收的,这个机制并不太优雅,可能会拖慢Shrinkers内存扫描速度,已从内核4.12中移除,后续会采用用户空间的LMKD + memory cgroups机制,这里先不展开LMK讲解。

进程刚启动时ADJ等于INVALID_ADJ,当执行完attachApplication(),该该进程的curAdj和setAdj不相等,则会触发执行setOomAdj()将该进程的节点/proc/pid/oom_score_adj写入oomadj值。下图参数为Android原生阈值,当系统剩余空闲内存低于某阈值(比如147MB),则从ADJ大于或等于相应阈值(比如900)的进程中,选择ADJ值最大的进程,如果存在多个ADJ相同的进程,则选择内存最大的进程。 如下是64位机器,LMK默认阈值图:

在updateOomLevels()过程,会根据手机屏幕尺寸或内存大小来调整scale,默认大多数手机内存都大于700MB,则scale等于1。对于64位手机,阈值会更大些,具体如下。

 private void updateOomLevels(int displayWidth, int displayHeight, boolean write) {// Scale buckets from avail memory: at 300MB we use the lowest values to// 700MB or more for the top values.float scaleMem = ((float)(mTotalMemMb-350))/(700-350);// Scale buckets from screen size.int minSize = 480*800;  //  384000int maxSize = 1280*800; // 1024000  230400 870400  .264float scaleDisp = ((float)(displayWidth*displayHeight)-minSize)/(maxSize-minSize);if (false) {Slog.i("XXXXXX", "scaleMem=" + scaleMem);Slog.i("XXXXXX", "scaleDisp=" + scaleDisp + " dw=" + displayWidth+ " dh=" + displayHeight);}float scale = scaleMem > scaleDisp ? scaleMem : scaleDisp;if (scale < 0) scale = 0;else if (scale > 1) scale = 1;int minfree_adj = Resources.getSystem().getInteger(com.android.internal.R.integer.config_lowMemoryKillerMinFreeKbytesAdjust);int minfree_abs = Resources.getSystem().getInteger(com.android.internal.R.integer.config_lowMemoryKillerMinFreeKbytesAbsolute);if (false) {Slog.i("XXXXXX", "minfree_adj=" + minfree_adj + " minfree_abs=" + minfree_abs);}final boolean is64bit = Build.SUPPORTED_64_BIT_ABIS.length > 0;for (int i=0; i<mOomAdj.length; i++) {int low = mOomMinFreeLow[i];int high = mOomMinFreeHigh[i];if (is64bit) {// Increase the high min-free levels for cached processes for 64-bitif (i == 4) high = (high*3)/2;else if (i == 5) high = (high*7)/4;}mOomMinFree[i] = (int)(low + ((high-low)*scale));}if (minfree_abs >= 0) {for (int i=0; i<mOomAdj.length; i++) {mOomMinFree[i] = (int)((float)minfree_abs * mOomMinFree[i]/ mOomMinFree[mOomAdj.length - 1]);}}if (minfree_adj != 0) {for (int i=0; i<mOomAdj.length; i++) {mOomMinFree[i] += (int)((float)minfree_adj * mOomMinFree[i]/ mOomMinFree[mOomAdj.length - 1]);if (mOomMinFree[i] < 0) {mOomMinFree[i] = 0;}}}// The maximum size we will restore a process from cached to background, when under// memory duress, is 1/3 the size we have reserved for kernel caches and other overhead// before killing background processes.mCachedRestoreLevel = (getMemLevel(ProcessList.CACHED_APP_MAX_ADJ)/1024) / 3;// Ask the kernel to try to keep enough memory free to allocate 3 full// screen 32bpp buffers without entering direct reclaim.int reserve = displayWidth * displayHeight * 4 * 3 / 1024;int reserve_adj = Resources.getSystem().getInteger(com.android.internal.R.integer.config_extraFreeKbytesAdjust);int reserve_abs = Resources.getSystem().getInteger(com.android.internal.R.integer.config_extraFreeKbytesAbsolute);if (reserve_abs >= 0) {reserve = reserve_abs;}if (reserve_adj != 0) {reserve += reserve_adj;if (reserve < 0) {reserve = 0;}}if (write) {ByteBuffer buf = ByteBuffer.allocate(4 * (2*mOomAdj.length + 1));buf.putInt(LMK_TARGET);for (int i=0; i<mOomAdj.length; i++) {buf.putInt((mOomMinFree[i]*1024)/PAGE_SIZE);buf.putInt(mOomAdj[i]);}writeLmkd(buf);SystemProperties.set("sys.sysctl.extra_free_kbytes", Integer.toString(reserve));}// GB: 2048,3072,4096,6144,7168,8192// HC: 8192,10240,12288,14336,16384,20480}

2.1.2 ProcessRecord

public final class ProcessRecord {private static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessRecord" : TAG_AM;private final BatteryStatsImpl mBatteryStats; // where to collect runtime statistics//管理进程电量统计public final ApplicationInfo info; // all about the first app in the processfinal boolean isolated;     // true if this is a special isolated process //独立进程public final int uid;       // uid of process; may be different from 'info' if isolatedfinal int userId;           // user of process.public final String processName;   // name of the process// List of packages running in the processpublic final ArrayMap<String, ProcessStats.ProcessStateHolder> pkgList = new ArrayMap<>();UidRecord uidRecord;        // overall state of process's uid.//进程运行依赖的包ArraySet<String> pkgDeps;   // additional packages we have a dependency on//保存IApplicationThread,通过它可以和应用进程交互public IApplicationThread thread;  // the actual proc...  may be null only if// 'persistent' is true (in which case we// are in the process of launching the app)ProcessState baseProcessTracker;BatteryStatsImpl.Uid.Proc curProcBatteryStats;public int pid;             // The process of this application; 0 if noneString procStatFile;        // path to /proc/<pid>/statint[] gids;                 // The gids this process was launched withString requiredAbi;         // The ABI this process was launched withString instructionSet;      // The instruction set this process was launched withboolean starting;           // True if the process is being startedlong lastActivityTime;      // For managing the LRU list//每次updateLruProcessLocked()过程会更新该值long lastPssTime;           // Last time we retrieved PSS datalong nextPssTime;           // Next time we want to request PSS datalong lastStateTime;         // Last time setProcState changedlong initialIdlePss;        // Initial memory pss of process for idle maintenance.long lastPss;               // Last computed memory pss.long lastSwapPss;           // Last computed SwapPss.long lastCachedPss;         // Last computed pss when in cached state.long lastCachedSwapPss;     // Last computed SwapPss when in cached state.//和oom_adj有关int maxAdj;                 // Maximum OOM adjustment for this process 进程的adj上限(adjustment)int curRawAdj;              // Current OOM unlimited adjustment for this process  当前正在计算的adj,这个值有可能大于maxAdjint setRawAdj;              // Last set OOM unlimited adjustment for this process 上次计算的curRawAdj设置到lowmemorykiller系统后的adjpublic int curAdj;          // Current OOM adjustment for this process 当前正在计算的adj,这是curRawAdj被maxAdj削平的值int setAdj;                 // Last set OOM adjustment for this process 上次计算的curAdj设置到lowmemorykiller系统后的adjint verifiedAdj;            // The last adjustment that was verified as actually being set//和调度优先级有关int curSchedGroup;          // Currently desired scheduling classint setSchedGroup;          // Last set to background scheduling classint vrThreadTid;            // Thread currently set for VR scheduling//回收内存级别//默认是80%,ComponentCallbacks2.TRIM_MEMORY_COMPLETEint trimMemoryLevel;        // Last selected memory trimming level//判断该进程的状态,主要和其中运行的Activity,Service有关  ActivityManager.PROCESS_STATEpublic int curProcState = PROCESS_STATE_NONEXISTENT; // Currently computed process stateint repProcState = PROCESS_STATE_NONEXISTENT; // Last reported process stateint setProcState = PROCESS_STATE_NONEXISTENT; // Last set process state in process trackerint pssProcState = PROCESS_STATE_NONEXISTENT; // Currently requesting pss forint savedPriority;          // Previous priority value if we're switching to non-SCHED_OTHERint renderThreadTid;        // TID for RenderThreadboolean serviceb;           // Process currently is on the service B listboolean serviceHighRam;     // We are forcing to service B list due to its RAM useboolean notCachedSinceIdle; // Has this process not been in a cached state since last idle?boolean hasClientActivities;  // Are there any client services with activities?boolean hasStartedServices; // Are there any started services running in this process?boolean foregroundServices; // Running any services that are foreground?boolean foregroundActivities; // Running any activities that are foreground?boolean repForegroundActivities; // Last reported foreground activities.//这个表示进程是否绑定的activity,通过activities.size的值和visible来判断,如果visible=true,就是falseboolean systemNoUi;         // This is a system process, but not currently showing UI.boolean hasShownUi;         // Has UI been shown in this process since it was started?
//显示顶层的uiboolean hasTopUi;           // Is this process currently showing a non-activity UI that the user// is interacting with? E.g. The status bar when it is expanded, but// not when it is minimized. When true the// process will be set to use the ProcessList#SCHED_GROUP_TOP_APP// scheduling group to boost performance.boolean hasOverlayUi;       // Is the process currently showing a non-activity UI that// overlays on-top of activity UIs on screen. E.g. display a window// of type// android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY// When true the process will oom adj score will be set to// ProcessList#PERCEPTIBLE_APP_ADJ at minimum to reduce the chance// of the process getting killed.boolean pendingUiClean;     // Want to clean up resources from showing UI?boolean hasAboveClient;     // Bound using BIND_ABOVE_CLIENT, so want to be lowerboolean treatLikeActivity;  // Bound using BIND_TREAT_LIKE_ACTIVITY//判断该进程的状态,主要和其中运行的Activity,Service有关boolean bad;                // True if disabled in the bad process list  是否处于系统BadProcess列表public boolean killedByAm;  // True when proc has been killed by activity manager, not for RAM//当值为true,意味着该进程是被AMS所杀,而非由于内存低而被LMK所杀public boolean killed;      // True once we know the process has been killed//进程被杀死boolean procStateChanged;   // Keep track of whether we changed 'setAdj'.boolean reportedInteraction;// Whether we have told usage stats about it being an interactionboolean unlocked;           // True when proc was started in user unlocked statelong interactionEventTime;  // The time we sent the last interaction eventlong fgInteractionTime;     // When we became foreground for interaction purposespublic String waitingToKill; // Process is waiting to be killed when in the bg, and reasonObject forcingToImportant;  // Token that is forcing this process to be important//序号,每次调节进程优先级或者LRU列表位置时,这些序号都会递增int adjSeq;                 // Sequence id for identifying oom_adj assignment cyclesint lruSeq;                 // Sequence id for identifying LRU update cyclesCompatibilityInfo compat;   // last used compatibility modeIBinder.DeathRecipient deathRecipient; // Who is watching for the death.//activity正在运行ActiveInstrumentation instr;// Set to currently active instrumentation running in processboolean usingWrapper;       // Set to true when process was launched with a wrapper attachedfinal ArraySet<BroadcastRecord> curReceivers = new ArraySet<BroadcastRecord>();// receivers currently running in the applong whenUnimportant;       // When (uptime) the process last became unimportantlong lastCpuTime;           // How long proc has run CPU at last checklong curCpuTime;            // How long proc has run CPU most recentlylong lastRequestedGc;       // When we last asked the app to do a gclong lastLowMemory;         // When we last told the app that memory is lowlong lastProviderTime;      // The last time someone else was using a provider in this process.boolean reportLowMemory;    // Set to true when waiting to report low memboolean empty;              // Is this an empty background process?boolean cached;             // Is this a cached process?String adjType;             // Debugging: primary thing impacting oom_adj.int adjTypeCode;            // Debugging: adj code to report to app.Object adjSource;           // Debugging: option dependent object.int adjSourceProcState;     // Debugging: proc state of adjSource's process.Object adjTarget;           // Debugging: target component impacting oom_adj.Runnable crashHandler;      // Optional local handler to be invoked in the process crash.// all activities running in the processfinal ArrayList<ActivityRecord> activities = new ArrayList<>();// all ServiceRecord running in this processfinal ArraySet<ServiceRecord> services = new ArraySet<>();// services that are currently executing code (need to remain foreground).final ArraySet<ServiceRecord> executingServices = new ArraySet<>();// All ConnectionRecord this process holdsfinal ArraySet<ConnectionRecord> connections = new ArraySet<>();// all IIntentReceivers that are registered from this process.final ArraySet<ReceiverList> receivers = new ArraySet<>();// class (String) -> ContentProviderRecord//保持providerfinal ArrayMap<String, ContentProviderRecord> pubProviders = new ArrayMap<>();// All ContentProviderRecord process is usingfinal ArrayList<ContentProviderConnection> conProviders = new ArrayList<>();//有相关的class运行在这个进程中String isolatedEntryPoint;  // Class to run on start if this is a special isolated process.String[] isolatedEntryPointArgs; // Arguments to pass to isolatedEntryPoint's main().boolean execServicesFg;     // do we need to be executing services in the foreground?boolean persistent;         // always keep this application running?//经常不被杀死,杀死也会被系统重新启动boolean crashing;           // are we in the process of crashing?Dialog crashDialog;         // dialog being displayed due to crash.boolean forceCrashReport;   // suppress normal auto-dismiss of crash dialog & report UI?boolean notResponding;      // does the app have a not responding dialog?Dialog anrDialog;           // dialog being displayed due to app not resp.boolean removed;            // has app package been removed from device?boolean debugging;          // was app launched for debugging?boolean waitedForDebugger;  // has process show wait for debugger dialog?Dialog waitDialog;          // current wait for debugger dialogString shortStringName;     // caching of toShortString() result.String stringName;          // caching of toString() result

ProcessRecord 记录了进程的所有信息,比如进程名,进程包含的activity ,service以及oom的阈值等有关进程的信息,具体可以看上面的代码块,有关此类的介绍可以看这篇博文:https://blog.csdn.net/tonyandroid1984/article/details/70224827

2.1.3 Process

 public static final int THREAD_PRIORITY_DEFAULT = 0;/** **************************************** ** Keep in sync with utils/threads.h *** ****************************************//*** Lowest available thread priority.  Only for those who really, really* don't want to run if anything else is happening.* Use with {@link #setThreadPriority(int)} and* {@link #setThreadPriority(int, int)}, <b>not</b> with the normal* {@link java.lang.Thread} class.*/public static final int THREAD_PRIORITY_LOWEST = 19;/*** Standard priority background threads.  This gives your thread a slightly* lower than normal priority, so that it will have less chance of impacting* the responsiveness of the user interface.* Use with {@link #setThreadPriority(int)} and* {@link #setThreadPriority(int, int)}, <b>not</b> with the normal* {@link java.lang.Thread} class.*/public static final int THREAD_PRIORITY_BACKGROUND = 10;/*** Standard priority of threads that are currently running a user interface* that the user is interacting with.  Applications can not normally* change to this priority; the system will automatically adjust your* application threads as the user moves through the UI.* Use with {@link #setThreadPriority(int)} and* {@link #setThreadPriority(int, int)}, <b>not</b> with the normal* {@link java.lang.Thread} class.*/public static final int THREAD_PRIORITY_FOREGROUND = -2;/*** Standard priority of system display threads, involved in updating* the user interface.  Applications can not* normally change to this priority.* Use with {@link #setThreadPriority(int)} and* {@link #setThreadPriority(int, int)}, <b>not</b> with the normal* {@link java.lang.Thread} class.*/public static final int THREAD_PRIORITY_DISPLAY = -4;/*** Standard priority of the most important display threads, for compositing* the screen and retrieving input events.  Applications can not normally* change to this priority.* Use with {@link #setThreadPriority(int)} and* {@link #setThreadPriority(int, int)}, <b>not</b> with the normal* {@link java.lang.Thread} class.*/public static final int THREAD_PRIORITY_URGENT_DISPLAY = -8;/*** Standard priority of audio threads.  Applications can not normally* change to this priority.* Use with {@link #setThreadPriority(int)} and* {@link #setThreadPriority(int, int)}, <b>not</b> with the normal* {@link java.lang.Thread} class.*/public static final int THREAD_PRIORITY_AUDIO = -16;/*** Standard priority of the most important audio threads.* Applications can not normally change to this priority.* Use with {@link #setThreadPriority(int)} and* {@link #setThreadPriority(int, int)}, <b>not</b> with the normal* {@link java.lang.Thread} class.*/public static final int THREAD_PRIORITY_URGENT_AUDIO = -19;/*** Minimum increment to make a priority more favorable.*/public static final int THREAD_PRIORITY_MORE_FAVORABLE = -1;/*** Minimum increment to make a priority less favorable.*/public static final int THREAD_PRIORITY_LESS_FAVORABLE = +1;/*** Default scheduling policy* @hide*/
//以下就是进程创建的调度策略public static final int SCHED_OTHER = 0;/*** First-In First-Out scheduling policy* @hide*/public static final int SCHED_FIFO = 1;/*** Round-Robin scheduling policy* @hide*/public static final int SCHED_RR = 2;/*** Batch scheduling policy* @hide*/public static final int SCHED_BATCH = 3;/*** Idle scheduling policy* @hide*/public static final int SCHED_IDLE = 5;//paid,tid 的获取public static final int myPid() {return Os.getpid();}/*** Returns the identifier of this process' parent.* @hide*/public static final int myPpid() {return Os.getppid();}/*** Returns the identifier of the calling thread, which be used with* {@link #setThreadPriority(int, int)}.*/public static final int myTid() {return Os.gettid();}/*** Returns the identifier of this process's uid.  This is the kernel uid* that the process is running under, which is the identity of its* app-specific sandbox.  It is different from {@link #myUserHandle} in that* a uid identifies a specific app sandbox in a specific user.*/public static final int myUid() {return Os.getuid();}//设置线程等级,也就是更大的可能获得cpu调度的时间。public static final native void setThreadPriority(int tid, int priority)throws IllegalArgumentException, SecurityException;public static final native void setThreadPriority(int priority)throws IllegalArgumentException, SecurityException;//设置线程的调度等级和策略,等级有-20到19  策略有SCHED_OTHER,SCHED_FIFO,SCHED_RRpublic static final native void setThreadScheduler(int tid, int policy, int priority)throws IllegalArgumentException;//group == THREAD_GROUP_FOREGROUNDpublic static final native void setProcessGroup(int pid, int group)throws IllegalArgumentException, SecurityException;/*** Return the scheduling group of requested process.** @hide*/public static final native int getProcessGroup(int pid)throws IllegalArgumentException, SecurityException;

以上的是代码块并没有包含全部的类信息,只是挑选一些比较重要的字段和函数来说明

2.1.4 updateLruProcessLocked

final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,ProcessRecord client) {final boolean hasActivity = app.activities.size() > 0 || app.hasClientActivities|| app.treatLikeActivity;final boolean hasService = false; // not impl yet. app.services.size() > 0;if (!activityChange && hasActivity) {// The process has activities, so we are only allowing activity-based adjustments// to move it.  It should be kept in the front of the list with other// processes that have activities, and we don't want those to change their// order except due to activity operations.return;}mLruSeq++;//每一次调整LRU列表,系统都会分配一个唯一的编号Log.i("ActivityManagerService-xiao", "updateLruProcessLocked app" +app+",,client :"+client+",activityChange:"+activityChange+",mLruProcessActivityStart:"+mLruProcessActivityStart+",mLruProcessServiceStart:"+mLruProcessServiceStart);   final long now = SystemClock.uptimeMillis();app.lastActivityTime = now;// First a quick reject: if the app is already at the position we will// put it, then there is nothing to do.if (hasActivity) {//进程包含ativity,并且已经在顶层就不需要调整final int N = mLruProcesses.size();if (N > 0 && mLruProcesses.get(N-1) == app) {if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, already top activity: " + app);return;}} else {//服务进程的情况if (mLruProcessServiceStart > 0&& mLruProcesses.get(mLruProcessServiceStart-1) == app) {if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, already top other: " + app);return;}}//获取app在mLruProcesses中的索引位置,如果进程是第一次加入的话lrui=-1int lrui = mLruProcesses.lastIndexOf(app);//如果是persistent进程就不做调整if (app.persistent && lrui >= 0) {// We don't care about the position of persistent processes, as long as// they are in the list.if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, persistent: " + app);return;}/* In progress: compute new position first, so we can avoid doing workif the process is not actually going to move.  Not yet working.int addIndex;int nextIndex;boolean inActivity = false, inService = false;if (hasActivity) {// Process has activities, put it at the very tipsy-top.addIndex = mLruProcesses.size();nextIndex = mLruProcessServiceStart;inActivity = true;} else if (hasService) {// Process has services, put it at the top of the service list.addIndex = mLruProcessActivityStart;nextIndex = mLruProcessServiceStart;inActivity = true;inService = true;} else  {// Process not otherwise of interest, it goes to the top of the non-service area.addIndex = mLruProcessServiceStart;if (client != null) {int clientIndex = mLruProcesses.lastIndexOf(client);if (clientIndex < 0) Slog.d(TAG, "Unknown client " + client + " when updating "+ app);if (clientIndex >= 0 && addIndex > clientIndex) {addIndex = clientIndex;}}nextIndex = addIndex > 0 ? addIndex-1 : addIndex;}Slog.d(TAG, "Update LRU at " + lrui + " to " + addIndex + " (act="+ mLruProcessActivityStart + "): " + app);*/if (lrui >= 0) {if (lrui < mLruProcessActivityStart) {mLruProcessActivityStart--;}if (lrui < mLruProcessServiceStart) {mLruProcessServiceStart--;}/*if (addIndex > lrui) {addIndex--;}if (nextIndex > lrui) {nextIndex--;}*///如果之前有记录,则先从数组中删掉,因为此处需要重新调整位置   mLruProcesses.remove(lrui);}/*mLruProcesses.add(addIndex, app);if (inActivity) {mLruProcessActivityStart++;}if (inService) {mLruProcessActivityStart++;}*/int nextIndex;if (hasActivity) {final int N = mLruProcesses.size();//此app进程没有活动的activity,但是其他进程含有此app的activityif (app.activities.size() == 0 && mLruProcessActivityStart < (N - 1)) {// Process doesn't have activities, but has clients with// activities...  move it up, but one below the top (the top// should always have a real activity).if (DEBUG_LRU) Slog.d(TAG_LRU,"Adding to second-top of LRU activity list: " + app);mLruProcesses.add(N - 1, app);//放在第二位// To keep it from spamming the LRU list (by making a bunch of clients),// we will push down any other entries owned by the app.final int uid = app.info.uid;//如果N-2后的进程与app        uid相同就向前移动一位for (int i = N - 2; i > mLruProcessActivityStart; i--) {ProcessRecord subProc = mLruProcesses.get(i);if (subProc.info.uid == uid) {// We want to push this one down the list.  If the process after// it is for the same uid, however, don't do so, because we don't// want them internally to be re-ordered.if (mLruProcesses.get(i - 1).info.uid != uid) {if (DEBUG_LRU) Slog.d(TAG_LRU,"Pushing uid " + uid + " swapping at " + i + ": "+ mLruProcesses.get(i) + " : " + mLruProcesses.get(i - 1));ProcessRecord tmp = mLruProcesses.get(i);mLruProcesses.set(i, mLruProcesses.get(i - 1));mLruProcesses.set(i - 1, tmp);i--;}} else {// A gap, we can stop here.break;}}} else {// Process has activities, put it at the very tipsy-top.if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU activity list: " + app);mLruProcesses.add(app);//有activity的进程就放在第第一位}nextIndex = mLruProcessServiceStart;} else if (hasService) {// Process has services, put it at the top of the service list.if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU service list: " + app);mLruProcesses.add(mLruProcessActivityStart, app);nextIndex = mLruProcessServiceStart;mLruProcessActivityStart++;} else  {//放在服务的顶层// Process not otherwise of interest, it goes to the top of the non-service area.int index = mLruProcessServiceStart;if (client != null) {// If there is a client, don't allow the process to be moved up higher// in the list than that client.int clientIndex = mLruProcesses.lastIndexOf(client);if (DEBUG_LRU && clientIndex < 0) Slog.d(TAG_LRU, "Unknown client " + client+ " when updating " + app);if (clientIndex <= lrui) {// Don't allow the client index restriction to push it down farther in the// list than it already is.clientIndex = lrui;}if (clientIndex >= 0 && index > clientIndex) {index = clientIndex;}}if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding at " + index + " of LRU list: " + app);mLruProcesses.add(index, app);nextIndex = index-1;mLruProcessActivityStart++;mLruProcessServiceStart++;}// If the app is currently using a content provider or service,// bump those processes as well.//如果该将app 绑定到其他service,则要对应调整Service和ContentProvider所在进程的LRUfor (int j=app.connections.size()-1; j>=0; j--) {ConnectionRecord cr = app.connections.valueAt(j);if (cr.binding != null && !cr.serviceDead && cr.binding.service != null&& cr.binding.service.app != null&& cr.binding.service.app.lruSeq != mLruSeq&& !cr.binding.service.app.persistent) {nextIndex = updateLruProcessInternalLocked(cr.binding.service.app, now, nextIndex,"service connection", cr, app);}}for (int j=app.conProviders.size()-1; j>=0; j--) {ContentProviderRecord cpr = app.conProviders.get(j).provider;if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.persistent) {nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex,"provider reference", cpr, app);}}}

从以上代码可知,updateLruProcessLocked的主要工作是根据app的lruWeight值调整它在数组中的位置。lruWeight值越大,其在数组中的位置就越靠后。如果该app和某些Service(仅考虑通过bindService建立关系的那些Service)或ContentProvider有交互关系,那么这些Service或ContentProvider所在的进程也需要调节lruWeight值。

2.1.5 updateOomAdjLocked

 final void updateOomAdjLocked() {//在一般情况下,resumedAppLocked返回 mResumedActivity,即当前正处于前台的Activityfinal ActivityRecord TOP_ACT = resumedAppLocked();final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;//前台进程final long now = SystemClock.uptimeMillis();final long nowElapsed = SystemClock.elapsedRealtime();final long oldTime = now - ProcessList.MAX_EMPTY_TIME;final int N = mLruProcesses.size();
Log.i("ActivityManagerService-xiao", "updateOomAdjLocked TOP_ACT:"+TOP_ACT+",TOP_APP:"+TOP_APP+",mLruProcesses.size():"+mLruProcesses.size() );if (false) {RuntimeException e = new RuntimeException();e.fillInStackTrace();Slog.i(TAG, "updateOomAdj: top=" + TOP_ACT, e);}// Reset state in all uid records.for (int i=mActiveUids.size()-1; i>=0; i--) {final UidRecord uidRec = mActiveUids.valueAt(i);if (false && DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,"Starting update of " + uidRec);uidRec.reset();}mStackSupervisor.rankTaskLayersIfNeeded();mAdjSeq++;mNewNumServiceProcs = 0;mNewNumAServiceProcs = 0;final int emptyProcessLimit = mConstants.CUR_MAX_EMPTY_PROCESSES;//系统运行存在最大的空的进程数final int cachedProcessLimit = mConstants.CUR_MAX_CACHED_PROCESSES - emptyProcessLimit;系统运行存在最大的缓存的进程数// Let's determine how many processes we have running vs.// how many slots we have for background processes; we may want// to put multiple processes in a slot of there are enough of// them./*下面这几句代码的作用如下:1 根据hidden adj划分级别,一共有9个级别(即numSlots值)2 根据mLruProcesses的成员个数计算平均落在各个级别的进程数*///numSlots=3int numSlots = (ProcessList.CACHED_APP_MAX_ADJ- ProcessList.CACHED_APP_MIN_ADJ + 1) / 2;int numEmptyProcs = N - mNumNonCachedProcs - mNumCachedHiddenProcs;if (numEmptyProcs > cachedProcessLimit) {// If there are more empty processes than our limit on cached// processes, then use the cached process limit for the factor.// This ensures that the really old empty processes get pushed// down to the bottom, so if we are running low on memory we will// have a better chance at keeping around more cached processes// instead of a gazillion empty processes.numEmptyProcs = cachedProcessLimit;}
//emptyFactor每组数量 , cachedFactor=mNumCachedHiddenProcs隐藏的进程,近期列表int emptyFactor = numEmptyProcs/numSlots;//每3个分一组if (emptyFactor < 1) emptyFactor = 1;int cachedFactor = (mNumCachedHiddenProcs > 0 ? mNumCachedHiddenProcs : 1)/numSlots;if (cachedFactor < 1) cachedFactor = 1;int stepCached = 0;int stepEmpty = 0;int numCached = 0;int numEmpty = 0;int numTrimming = 0;mNumNonCachedProcs = 0;mNumCachedHiddenProcs = 0;// First update the OOM adjustment for each of the// application processes based on their current state.int curCachedAdj = ProcessList.CACHED_APP_MIN_ADJ;int nextCachedAdj = curCachedAdj+1;int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ;int nextEmptyAdj = curEmptyAdj+2;for (int i=N-1; i>=0; i--) {ProcessRecord app = mLruProcesses.get(i);if (!app.killedByAm && app.thread != null) {//进程还活着,没有被杀死app.procStateChanged = false;computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now);// If we haven't yet assigned the final cached adj// to the process, do that now.//app.curAdj >= ProcessList.UNKNOWN_ADJ的情况if (app.curAdj >= ProcessList.UNKNOWN_ADJ) {switch (app.curProcState) {case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:// This process is a cached process holding activities...// assign it the next cached value for that type, and then// step that cached level.app.curRawAdj = curCachedAdj;app.curAdj = app.modifyRawOomAdj(curCachedAdj);if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning activity LRU #" + i+ " adj: " + app.curAdj + " (curCachedAdj=" + curCachedAdj+ ")");if (curCachedAdj != nextCachedAdj) {stepCached++;if (stepCached >= cachedFactor) {stepCached = 0;curCachedAdj = nextCachedAdj;nextCachedAdj += 2;if (nextCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {nextCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;}}}break;default:// For everything else, assign next empty cached process// level and bump that up.  Note that this means that// long-running services that have dropped down to the// cached level will be treated as empty (since their process// state is still as a service), which is what we want.app.curRawAdj = curEmptyAdj;app.curAdj = app.modifyRawOomAdj(curEmptyAdj);if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning empty LRU #" + i+ " adj: " + app.curAdj + " (curEmptyAdj=" + curEmptyAdj+ ")");if (curEmptyAdj != nextEmptyAdj) {stepEmpty++;if (stepEmpty >= emptyFactor) {stepEmpty = 0;curEmptyAdj = nextEmptyAdj;nextEmptyAdj += 2;if (nextEmptyAdj > ProcessList.CACHED_APP_MAX_ADJ) {nextEmptyAdj = ProcessList.CACHED_APP_MAX_ADJ;}}}break;}}applyOomAdjLocked(app, true, now, nowElapsed);// Count the number of process types.//计算隐藏Hidden的进程数量,计算的目的是为了判断这个进程是否超过数量switch (app.curProcState) {case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:mNumCachedHiddenProcs++;numCached++;//如果隐藏的数量大于限制就会被杀死if (numCached > cachedProcessLimit) {app.kill("cached #" + numCached, true);}break;case ActivityManager.PROCESS_STATE_CACHED_EMPTY://空进程数量if (numEmpty > mConstants.CUR_TRIM_EMPTY_PROCESSES&& app.lastActivityTime < oldTime) {app.kill("empty for "+ ((oldTime + ProcessList.MAX_EMPTY_TIME - app.lastActivityTime)/ 1000) + "s", true);} else {numEmpty++;if (numEmpty > emptyProcessLimit) {app.kill("empty #" + numEmpty, true);}}break;default:mNumNonCachedProcs++;break;}//独立的进程,并且还没有绑定service, app.isolatedEntryPoint是表示有没有相关的运行代码if (app.isolated && app.services.size() <= 0 && app.isolatedEntryPoint == null) {// If this is an isolated process, there are no services// running in it, and it's not a special process with a// custom entry point, then the process is no longer// needed.  We agressively kill these because we can by// definition not re-use the same process again, and it is// good to avoid having whatever code was running in them// left sitting around after no longer needed.app.kill("isolated not needed", true);} else {// Keeping this process, update its uid.final UidRecord uidRec = app.uidRecord;if (uidRec != null) {uidRec.ephemeral = app.info.isInstantApp();if (uidRec.curProcState > app.curProcState) {uidRec.curProcState = app.curProcState;}if (app.foregroundServices) {uidRec.foregroundServices = true;}}}if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME&& !app.killedByAm) {numTrimming++;}}}incrementProcStateSeqAndNotifyAppsLocked();mNumServiceProcs = mNewNumServiceProcs;// Now determine the memory trimming level of background processes.// Unfortunately we need to start at the back of the list to do this// properly.  We only do this if the number of background apps we// are managing to keep around is less than half the maximum we desire;// if we are keeping a good number around, we'll let them use whatever// memory they want.final int numCachedAndEmpty = numCached + numEmpty;int memFactor;//内存状态if (numCached <= mConstants.CUR_TRIM_CACHED_PROCESSES&& numEmpty <= mConstants.CUR_TRIM_EMPTY_PROCESSES) {if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) {memFactor = ProcessStats.ADJ_MEM_FACTOR_CRITICAL;} else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) {memFactor = ProcessStats.ADJ_MEM_FACTOR_LOW;} else {memFactor = ProcessStats.ADJ_MEM_FACTOR_MODERATE;}} else {memFactor = ProcessStats.ADJ_MEM_FACTOR_NORMAL;}// We always allow the memory level to go up (better).  We only allow it to go// down if we are in a state where that is allowed, *and* the total number of processes// has gone down since last time.if (DEBUG_OOM_ADJ) Slog.d(TAG_OOM_ADJ, "updateOomAdjLocked oom: memFactor=" + memFactor+ " last=" + mLastMemoryLevel + " allowLow=" + mAllowLowerMemLevel+ " numProcs=" + mLruProcesses.size() + " last=" + mLastNumProcesses);Log.d("ActivityManagerService-xiao", "oom: memFactor=" + memFactor+ " last=" + mLastMemoryLevel + " allowLow=" + mAllowLowerMemLevel+ " numProcs=" + mLruProcesses.size() + " last=" + mLastNumProcesses);if (memFactor > mLastMemoryLevel) {if (!mAllowLowerMemLevel || mLruProcesses.size() >= mLastNumProcesses) {memFactor = mLastMemoryLevel;if (DEBUG_OOM_ADJ) Slog.d(TAG_OOM_ADJ, "Keeping last mem factor!");}}if (memFactor != mLastMemoryLevel) {EventLogTags.writeAmMemFactor(memFactor, mLastMemoryLevel);}mLastMemoryLevel = memFactor;mLastNumProcesses = mLruProcesses.size();boolean allChanged = mProcessStats.setMemFactorLocked(memFactor, !isSleepingLocked(), now);final int trackerMemFactor = mProcessStats.getMemFactorLocked();if (memFactor != ProcessStats.ADJ_MEM_FACTOR_NORMAL) {if (mLowRamStartTime == 0) {mLowRamStartTime = now;}int step = 0;int fgTrimLevel;switch (memFactor) {case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL;break;case ProcessStats.ADJ_MEM_FACTOR_LOW:fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;break;default:fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE;break;}//根据mLruProcesses的成员个数计算平均落在各个级别的进程数(即factor值)int factor = numTrimming/3;int minFactor = 2;if (mHomeProcess != null) minFactor++;if (mPreviousProcess != null) minFactor++;if (factor < minFactor) factor = minFactor;int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;//下面是计算触发GC的内存阈值for (int i=N-1; i>=0; i--) {ProcessRecord app = mLruProcesses.get(i);if (allChanged || app.procStateChanged) {setProcessTrackerStateLocked(app, trackerMemFactor, now);app.procStateChanged = false;}if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME&& !app.killedByAm) {if (app.trimMemoryLevel < curLevel && app.thread != null) {try {if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,"Trimming memory of " + app.processName + " to " + curLevel);app.thread.scheduleTrimMemory(curLevel);} catch (RemoteException e) {}if (false) {// For now we won't do this; our memory trimming seems// to be good enough at this point that destroying// activities causes more harm than good.if (curLevel >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE&& app != mHomeProcess && app != mPreviousProcess) {// Need to do this on its own message because the stack may not// be in a consistent state at this point.// For these apps we will also finish their activities// to help them free memory.mStackSupervisor.scheduleDestroyAllActivities(app, "trim");}}}app.trimMemoryLevel = curLevel;step++;if (step >= factor) {step = 0;switch (curLevel) {case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:curLevel = ComponentCallbacks2.TRIM_MEMORY_MODERATE;break;case ComponentCallbacks2.TRIM_MEMORY_MODERATE:curLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;break;}}} else if (app.curProcState == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND&& app.thread != null) {try {if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,"Trimming memory of heavy-weight " + app.processName+ " to " + ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);app.thread.scheduleTrimMemory(ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);} catch (RemoteException e) {}}app.trimMemoryLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;} else {if ((app.curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND|| app.systemNoUi) && app.pendingUiClean) {// If this application is now in the background and it// had done UI, then give it the special trim level to// have it free UI resources.final int level = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN;if (app.trimMemoryLevel < level && app.thread != null) {try {if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,"Trimming memory of bg-ui " + app.processName+ " to " + level);app.thread.scheduleTrimMemory(level);} catch (RemoteException e) {}}app.pendingUiClean = false;}if (app.trimMemoryLevel < fgTrimLevel && app.thread != null) {try {if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,"Trimming memory of fg " + app.processName+ " to " + fgTrimLevel);app.thread.scheduleTrimMemory(fgTrimLevel);} catch (RemoteException e) {}}app.trimMemoryLevel = fgTrimLevel;}}} else {if (mLowRamStartTime != 0) {mLowRamTimeSinceLastIdle += now - mLowRamStartTime;mLowRamStartTime = 0;}for (int i=N-1; i>=0; i--) {ProcessRecord app = mLruProcesses.get(i);if (allChanged || app.procStateChanged) {setProcessTrackerStateLocked(app, trackerMemFactor, now);app.procStateChanged = false;}if ((app.curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND|| app.systemNoUi) && app.pendingUiClean) {if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN&& app.thread != null) {try {if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,"Trimming memory of ui hidden " + app.processName+ " to " + ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);app.thread.scheduleTrimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);} catch (RemoteException e) {}}app.pendingUiClean = false;}app.trimMemoryLevel = 0;}}if (mAlwaysFinishActivities) {// Need to do this on its own message because the stack may not// be in a consistent state at this point.mStackSupervisor.scheduleDestroyAllActivities(null, "always-finish");}if (allChanged) {requestPssAllProcsLocked(now, false, mProcessStats.isMemFactorLowered());}ArrayList<UidRecord> becameIdle = null;// Update from any uid changes.if (mLocalPowerManager != null) {mLocalPowerManager.startUidChanges();}for (int i=mActiveUids.size()-1; i>=0; i--) {final UidRecord uidRec = mActiveUids.valueAt(i);int uidChange = UidRecord.CHANGE_PROCSTATE;if (uidRec.curProcState != ActivityManager.PROCESS_STATE_NONEXISTENT&& (uidRec.setProcState != uidRec.curProcState|| uidRec.setWhitelist != uidRec.curWhitelist)) {if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,"Changes in " + uidRec + ": proc state from " + uidRec.setProcState+ " to " + uidRec.curProcState + ", whitelist from " + uidRec.setWhitelist+ " to " + uidRec.curWhitelist);if (ActivityManager.isProcStateBackground(uidRec.curProcState)&& !uidRec.curWhitelist) {// UID is now in the background (and not on the temp whitelist).  Was it// previously in the foreground (or on the temp whitelist)?if (!ActivityManager.isProcStateBackground(uidRec.setProcState)|| uidRec.setWhitelist) {uidRec.lastBackgroundTime = nowElapsed;if (!mHandler.hasMessages(IDLE_UIDS_MSG)) {// Note: the background settle time is in elapsed realtime, while// the handler time base is uptime.  All this means is that we may// stop background uids later than we had intended, but that only// happens because the device was sleeping so we are okay anyway.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,mConstants.BACKGROUND_SETTLE_TIME);}}if (uidRec.idle && !uidRec.setIdle) {uidChange = UidRecord.CHANGE_IDLE;if (becameIdle == null) {becameIdle = new ArrayList<>();}becameIdle.add(uidRec);}} else {if (uidRec.idle) {uidChange = UidRecord.CHANGE_ACTIVE;EventLogTags.writeAmUidActive(uidRec.uid);uidRec.idle = false;}uidRec.lastBackgroundTime = 0;}final boolean wasCached = uidRec.setProcState> ActivityManager.PROCESS_STATE_RECEIVER;final boolean isCached = uidRec.curProcState> ActivityManager.PROCESS_STATE_RECEIVER;if (wasCached != isCached ||uidRec.setProcState == ActivityManager.PROCESS_STATE_NONEXISTENT) {uidChange |= isCached ? UidRecord.CHANGE_CACHED : UidRecord.CHANGE_UNCACHED;}uidRec.setProcState = uidRec.curProcState;uidRec.setWhitelist = uidRec.curWhitelist;uidRec.setIdle = uidRec.idle;enqueueUidChangeLocked(uidRec, -1, uidChange);noteUidProcessState(uidRec.uid, uidRec.curProcState);if (uidRec.foregroundServices) {mServices.foregroundServiceProcStateChangedLocked(uidRec);}}}if (mLocalPowerManager != null) {mLocalPowerManager.finishUidChanges();}if (becameIdle != null) {// If we have any new uids that became idle this time, we need to make sure// they aren't left with running services.for (int i = becameIdle.size() - 1; i >= 0; i--) {mServices.stopInBackgroundLocked(becameIdle.get(i).uid);}}if (mProcessStats.shouldWriteNowLocked(now)) {mHandler.post(new Runnable() {@Override public void run() {synchronized (ActivityManagerService.this) {mProcessStats.writeStateAsyncLocked();}}});}if (DEBUG_OOM_ADJ) {final long duration = SystemClock.uptimeMillis() - now;if (false) {Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms",new RuntimeException("here").fillInStackTrace());} else {Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms");}}}

上面的代码中涉及到两个比较重要的函数computeOomAdjLocked和applyOomAdjLocked从名字大概就能猜出来他们的作用,一个计算adj的值,一个是以及计算出来adj的值来判断此app是否需要内存回收。computeOomAdjLocked后面会详讲。

上面函数主要是根据computeOomAdjLocked计算的进程adj值和curProcState来杀死一些隐藏或者空的进程。依据空进程的数量多少来判断内存的状态最后来判断是否需要GC

2.1.6 computeOomAdjLocked

private final int computeOomAdjLocked(ProcessRecord app, int cachedAdj, ProcessRecord TOP_APP,boolean doingAll, long now) {if (mAdjSeq == app.adjSeq) {// This adjustment has already been computed.return app.curRawAdj;}if (app.thread == null) {app.adjSeq = mAdjSeq;app.curSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND;app.curProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;return (app.curAdj=app.curRawAdj=ProcessList.CACHED_APP_MAX_ADJ);}app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN;app.adjSource = null;app.adjTarget = null;app.empty = false;app.cached = false;final int activitiesSize = app.activities.size();//该应用进程包含Activity的个数//如果maxAdj小于FOREGROUND_APP_ADJ,这类进程优先级相当高,一般属于系统进程if (app.maxAdj <= ProcessList.FOREGROUND_APP_ADJ) {// The max adjustment doesn't allow this app to be anything// below foreground, so it is not worth doing work for it.if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Making fixed: " + app);app.adjType = "fixed";app.adjSeq = mAdjSeq;app.curRawAdj = app.maxAdj;app.foregroundActivities = false;app.curSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT;//persistent进程// System processes can do UI, and when they do we want to have// them trim their memory after the user leaves the UI.  To// facilitate this, here we need to determine whether or not it// is currently showing UI.app.systemNoUi = true;if (app == TOP_APP) {//如果app在顶层,那就说明此进程具有UI,systemNoUi=false并且重新设置进程组app.systemNoUi = false;app.curSchedGroup = ProcessList.SCHED_GROUP_TOP_APP;app.adjType = "pers-top-activity";} else if (app.hasTopUi) {app.systemNoUi = false;app.curSchedGroup = ProcessList.SCHED_GROUP_TOP_APP;app.adjType = "pers-top-ui";} else if (activitiesSize > 0) {for (int j = 0; j < activitiesSize; j++) {final ActivityRecord r = app.activities.get(j);if (r.visible) {app.systemNoUi = false;}}}if (!app.systemNoUi) {app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT_UI;}return (app.curAdj=app.maxAdj);}app.systemNoUi = false;final int PROCESS_STATE_CUR_TOP = mTopProcessState;// Determine the importance of the process, starting with most// important to least, and assign an appropriate OOM adjustment.int adj;int schedGroup;int procState;boolean foregroundActivities = false;mTmpBroadcastQueue.clear();//如果app为前台Activity所在的那个应用进程if (app == TOP_APP) {// The last app on the list is the foreground app.adj = ProcessList.FOREGROUND_APP_ADJ;schedGroup = ProcessList.SCHED_GROUP_TOP_APP;app.adjType = "top-activity";foregroundActivities = true;procState = PROCESS_STATE_CUR_TOP;if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Making top: " + app);} else if (app.instr != null) {//activity正在启动,但是没有在最前台// Don't want to kill running instrumentation.adj = ProcessList.FOREGROUND_APP_ADJ;schedGroup = ProcessList.SCHED_GROUP_DEFAULT;app.adjType = "instrumentation";procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Making instrumentation: " + app);} else if (isReceivingBroadcastLocked(app, mTmpBroadcastQueue)) {// An app that is currently receiving a broadcast also// counts as being in the foreground for OOM killer purposes.// It's placed in a sched group based on the nature of the// broadcast as reflected by which queue it's active in.//此情况对应正在执行onReceive函数的广播接收者所在进程,它的优先级也很高adj = ProcessList.FOREGROUND_APP_ADJ;schedGroup = (mTmpBroadcastQueue.contains(mFgBroadcastQueue))? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;app.adjType = "broadcast";procState = ActivityManager.PROCESS_STATE_RECEIVER;if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Making broadcast: " + app);} else if (app.executingServices.size() > 0) {// An app that is currently executing a service callback also// counts as being in the foreground.//正在执行Service生命周期函数的进程adj = ProcessList.FOREGROUND_APP_ADJ;schedGroup = app.execServicesFg ?ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;app.adjType = "exec-service";procState = ActivityManager.PROCESS_STATE_SERVICE;if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Making exec-service: " + app);//Slog.i(TAG, "EXEC " + (app.execServicesFg ? "FG" : "BG") + ": " + app);} else {// As far as we know the process is empty.  We may change our mind later.//其他的都是后台进程schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;// At this point we don't actually know the adjustment.  Use the cached adj// value that the caller wants us to.adj = cachedAdj;procState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;app.cached = true;app.empty = true;app.adjType = "cch-empty";if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Making empty: " + app);}// Examine all activities if not already foreground.//检查进程中无前台Activity所在进程的处理if (!foregroundActivities && activitiesSize > 0) {int minLayer = ProcessList.VISIBLE_APP_LAYER_MAX;for (int j = 0; j < activitiesSize; j++) {final ActivityRecord r = app.activities.get(j);if (r.app != app) {Log.e(TAG, "Found activity " + r + " in proc activity list using " + r.app+ " instead of expected " + app);if (r.app == null || (r.app.uid == app.uid)) {// Only fix things up when they look saner.app = app;} else {continue;}}if (r.visible) {// App has a visible activity; only upgrade adjustment.//纠正一个可见的app 进程adj的值以及procState的值if (adj > ProcessList.VISIBLE_APP_ADJ) {adj = ProcessList.VISIBLE_APP_ADJ;app.adjType = "vis-activity";if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Raise to vis-activity: " + app);}if (procState > PROCESS_STATE_CUR_TOP) {procState = PROCESS_STATE_CUR_TOP;app.adjType = "vis-activity";if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Raise to vis-activity: " + app);}schedGroup = ProcessList.SCHED_GROUP_DEFAULT;app.cached = false;app.empty = false;foregroundActivities = true;final TaskRecord task = r.getTask();if (task != null && minLayer > 0) {final int layer = task.mLayerRank;if (layer >= 0 && minLayer > layer) {minLayer = layer;}}break;} else if (r.state == ActivityState.PAUSING || r.state == ActivityState.PAUSED) {if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {//暂停状态adj = ProcessList.PERCEPTIBLE_APP_ADJ;app.adjType = "pause-activity";if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Raise to pause-activity: " + app);}if (procState > PROCESS_STATE_CUR_TOP) {procState = PROCESS_STATE_CUR_TOP;app.adjType = "pause-activity";if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Raise to pause-activity: " + app);}schedGroup = ProcessList.SCHED_GROUP_DEFAULT;app.cached = false;app.empty = false;foregroundActivities = true;} else if (r.state == ActivityState.STOPPING) {//停止状态if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {adj = ProcessList.PERCEPTIBLE_APP_ADJ;app.adjType = "stop-activity";if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Raise to stop-activity: " + app);}// For the process state, we will at this point consider the// process to be cached.  It will be cached either as an activity// or empty depending on whether the activity is finishing.  We do// this so that we can treat the process as cached for purposes of// memory trimming (determing current memory level, trim command to// send to process) since there can be an arbitrary number of stopping// processes and they should soon all go into the cached state.if (!r.finishing) {//停止但是还没有finishif (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;app.adjType = "stop-activity";if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Raise to stop-activity: " + app);}}app.cached = false;app.empty = false;foregroundActivities = true;} else {if (procState > ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) {procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;app.adjType = "cch-act";if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Raise to cached activity: " + app);}}}if (adj == ProcessList.VISIBLE_APP_ADJ) {adj += minLayer;}}//刚刚暂停,进程状态是前台服务,比如musicif (adj > ProcessList.PERCEPTIBLE_APP_ADJ|| procState > ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {if (app.foregroundServices) {//前台服务// The user is aware of this app, so make it visible.adj = ProcessList.PERCEPTIBLE_APP_ADJ;procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;app.cached = false;app.adjType = "fg-service";schedGroup = ProcessList.SCHED_GROUP_DEFAULT;if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Raise to fg service: " + app);} else if (app.hasOverlayUi) {//有UI的但是目前又不可见// The process is display an overlay UI.adj = ProcessList.PERCEPTIBLE_APP_ADJ;procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;app.cached = false;app.adjType = "has-overlay-ui";schedGroup = ProcessList.SCHED_GROUP_DEFAULT;if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Raise to overlay ui: " + app);}}//刚暂停但是后台服务还在运行,if (adj > ProcessList.PERCEPTIBLE_APP_ADJ|| procState > ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) {if (app.forcingToImportant != null) {// This is currently used for toasts...  they are not interactive, and// we don't want them to cause the app to become fully foreground (and// thus out of background check), so we yes the best background level we can.adj = ProcessList.PERCEPTIBLE_APP_ADJ;procState = ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;app.cached = false;app.adjType = "force-imp";app.adjSource = app.forcingToImportant;schedGroup = ProcessList.SCHED_GROUP_DEFAULT;if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Raise to force imp: " + app);}}//如果进程是heavy-weight 进程if (app == mHeavyWeightProcess) {if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ) {// We don't want to kill the current heavy-weight process.adj = ProcessList.HEAVY_WEIGHT_APP_ADJ;schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;app.cached = false;app.adjType = "heavy";if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Raise to heavy: " + app);}if (procState > ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {procState = ActivityManager.PROCESS_STATE_HEAVY_WEIGHT;app.adjType = "heavy";if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Raise to heavy: " + app);}}//如果前面计算出来的adj大于HOME_APP_ADJ,并且该进程又是Home进程,则需要重新调整if (app == mHomeProcess) {if (adj > ProcessList.HOME_APP_ADJ) {// This process is hosting what we currently consider to be the// home app, so we don't want to let it go into the background.adj = ProcessList.HOME_APP_ADJ;schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;app.cached = false;app.adjType = "home";if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Raise to home: " + app);}if (procState > ActivityManager.PROCESS_STATE_HOME) {procState = ActivityManager.PROCESS_STATE_HOME;app.adjType = "home";if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Raise to home: " + app);}}//上次运行的app,就是显示在近期列表里面if (app == mPreviousProcess && app.activities.size() > 0) {if (adj > ProcessList.PREVIOUS_APP_ADJ) {// This was the previous process that showed UI to the user.// We want to try to keep it around more aggressively, to give// a good experience around switching between two apps.adj = ProcessList.PREVIOUS_APP_ADJ;schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;app.cached = false;app.adjType = "previous";if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Raise to prev: " + app);}if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;app.adjType = "previous";if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Raise to prev: " + app);}}if (false) Slog.i(TAG, "OOM " + app + ": initial adj=" + adj+ " reason=" + app.adjType);// By default, we use the computed adjustment.  It may be changed if// there are applications dependent on our services or providers, but// this gives us a baseline and makes sure we don't get into an// infinite recursion.app.adjSeq = mAdjSeq;app.curRawAdj = adj;app.hasStartedServices = false;//后台进程if (mBackupTarget != null && app == mBackupTarget.app) {// If possible we want to avoid killing apps while they're being backed upif (adj > ProcessList.BACKUP_APP_ADJ) {if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "oom BACKUP_APP_ADJ for " + app);adj = ProcessList.BACKUP_APP_ADJ;if (procState > ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) {procState = ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;}app.adjType = "backup";if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Raise to backup: " + app);app.cached = false;}if (procState > ActivityManager.PROCESS_STATE_BACKUP) {procState = ActivityManager.PROCESS_STATE_BACKUP;app.adjType = "backup";if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Raise to backup: " + app);}}boolean mayBeTop = false;String mayBeTopType = null;Object mayBeTopSource = null;Object mayBeTopTarget = null;//下面这几段代码处理那些进程中含有Service,ContentProvider组件情况下的adj调节for (int is = app.services.size()-1;is >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ|| schedGroup == ProcessList.SCHED_GROUP_BACKGROUND|| procState > ActivityManager.PROCESS_STATE_TOP);is--) {ServiceRecord s = app.services.valueAt(is);if (s.startRequested) {app.hasStartedServices = true;if (procState > ActivityManager.PROCESS_STATE_SERVICE) {procState = ActivityManager.PROCESS_STATE_SERVICE;app.adjType = "started-services";if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Raise to started service: " + app);}if (app.hasShownUi && app != mHomeProcess) {// If this process has shown some UI, let it immediately// go to the LRU list because it may be pretty heavy with// UI stuff.  We'll tag it with a label just to help// debug and understand what is going on.if (adj > ProcessList.SERVICE_ADJ) {app.adjType = "cch-started-ui-services";}} else {if (now < (s.lastActivity + mConstants.MAX_SERVICE_INACTIVITY)) {// This service has seen some activity within// recent memory, so we will keep its process ahead// of the background processes.if (adj > ProcessList.SERVICE_ADJ) {adj = ProcessList.SERVICE_ADJ;app.adjType = "started-services";if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Raise to started service: " + app);app.cached = false;}}// If we have let the service slide into the background// state, still have some text describing what it is doing// even though the service no longer has an impact.if (adj > ProcessList.SERVICE_ADJ) {app.adjType = "cch-started-services";}}}for (int conni = s.connections.size()-1;conni >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ|| schedGroup == ProcessList.SCHED_GROUP_BACKGROUND|| procState > ActivityManager.PROCESS_STATE_TOP);conni--) {ArrayList<ConnectionRecord> clist = s.connections.valueAt(conni);for (int i = 0;i < clist.size() && (adj > ProcessList.FOREGROUND_APP_ADJ|| schedGroup == ProcessList.SCHED_GROUP_BACKGROUND|| procState > ActivityManager.PROCESS_STATE_TOP);i++) {// XXX should compute this based on the max of// all connected clients.ConnectionRecord cr = clist.get(i);if (cr.binding.client == app) {// Binding to ourself is not interesting.continue;}if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) {ProcessRecord client = cr.binding.client;int clientAdj = computeOomAdjLocked(client, cachedAdj,TOP_APP, doingAll, now);int clientProcState = client.curProcState;if (clientProcState >= ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) {// If the other app is cached for any reason, for purposes here// we are going to consider it empty.  The specific cached state// doesn't propagate except under certain conditions.clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;}String adjType = null;if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {// Not doing bind OOM management, so treat// this guy more like a started service.if (app.hasShownUi && app != mHomeProcess) {// If this process has shown some UI, let it immediately// go to the LRU list because it may be pretty heavy with// UI stuff.  We'll tag it with a label just to help// debug and understand what is going on.if (adj > clientAdj) {adjType = "cch-bound-ui-services";}app.cached = false;clientAdj = adj;clientProcState = procState;} else {if (now >= (s.lastActivity + mConstants.MAX_SERVICE_INACTIVITY)) {// This service has not seen activity within// recent memory, so allow it to drop to the// LRU list if there is no other reason to keep// it around.  We'll also tag it with a label just// to help debug and undertand what is going on.if (adj > clientAdj) {adjType = "cch-bound-services";}clientAdj = adj;}}}if (adj > clientAdj) {// If this process has recently shown UI, and// the process that is binding to it is less// important than being visible, then we don't// care about the binding as much as we care// about letting this process get into the LRU// list to be killed and restarted if needed for// memory.if (app.hasShownUi && app != mHomeProcess&& clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {if (adj >= ProcessList.CACHED_APP_MIN_ADJ) {adjType = "cch-bound-ui-services";}} else {int newAdj;if ((cr.flags&(Context.BIND_ABOVE_CLIENT|Context.BIND_IMPORTANT)) != 0) {newAdj = clientAdj >= ProcessList.PERSISTENT_SERVICE_ADJ? clientAdj : ProcessList.PERSISTENT_SERVICE_ADJ;} else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0&& clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ&& adj > ProcessList.PERCEPTIBLE_APP_ADJ) {newAdj = ProcessList.PERCEPTIBLE_APP_ADJ;} else if (clientAdj >= ProcessList.PERCEPTIBLE_APP_ADJ) {newAdj = clientAdj;} else {if (adj > ProcessList.VISIBLE_APP_ADJ) {newAdj = Math.max(clientAdj, ProcessList.VISIBLE_APP_ADJ);} else {newAdj = adj;}}if (!client.cached) {app.cached = false;}if (adj >  newAdj) {adj = newAdj;adjType = "service";}}}if ((cr.flags & (Context.BIND_NOT_FOREGROUND| Context.BIND_IMPORTANT_BACKGROUND)) == 0) {// This will treat important bound services identically to// the top app, which may behave differently than generic// foreground work.if (client.curSchedGroup > schedGroup) {if ((cr.flags&Context.BIND_IMPORTANT) != 0) {schedGroup = client.curSchedGroup;} else {schedGroup = ProcessList.SCHED_GROUP_DEFAULT;}}if (clientProcState <= ActivityManager.PROCESS_STATE_TOP) {if (clientProcState == ActivityManager.PROCESS_STATE_TOP) {// Special handling of clients who are in the top state.// We *may* want to consider this process to be in the// top state as well, but only if there is not another// reason for it to be running.  Being on the top is a// special state, meaning you are specifically running// for the current top app.  If the process is already// running in the background for some other reason, it// is more important to continue considering it to be// in the background state.mayBeTop = true;mayBeTopType = "service";mayBeTopSource = cr.binding.client;mayBeTopTarget = s.name;clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;} else {// Special handling for above-top states (persistent// processes).  These should not bring the current process// into the top state, since they are not on top.  Instead// give them the best state after that.if ((cr.flags&Context.BIND_FOREGROUND_SERVICE) != 0) {clientProcState =ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;} else if (mWakefulness== PowerManagerInternal.WAKEFULNESS_AWAKE &&(cr.flags&Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE)!= 0) {clientProcState =ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;} else {clientProcState =ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;}}}} else if ((cr.flags & Context.BIND_IMPORTANT_BACKGROUND) == 0) {if (clientProcState <ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) {clientProcState =ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;}} else {if (clientProcState <ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) {clientProcState =ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;}}if (procState > clientProcState) {procState = clientProcState;if (adjType == null) {adjType = "service";}}if (procState < ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND&& (cr.flags&Context.BIND_SHOWING_UI) != 0) {app.pendingUiClean = true;}if (adjType != null) {app.adjType = adjType;app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_SERVICE_IN_USE;app.adjSource = cr.binding.client;app.adjSourceProcState = clientProcState;app.adjTarget = s.name;if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Raise to " + adjType+ ": " + app + ", due to " + cr.binding.client+ " adj=" + adj + " procState=" + procState);}}if ((cr.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {app.treatLikeActivity = true;}final ActivityRecord a = cr.activity;if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ &&(a.visible || a.state == ActivityState.RESUMED ||a.state == ActivityState.PAUSING)) {adj = ProcessList.FOREGROUND_APP_ADJ;if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {if ((cr.flags&Context.BIND_IMPORTANT) != 0) {schedGroup = ProcessList.SCHED_GROUP_TOP_APP_BOUND;} else {schedGroup = ProcessList.SCHED_GROUP_DEFAULT;}}app.cached = false;app.adjType = "service";app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_SERVICE_IN_USE;app.adjSource = a;app.adjSourceProcState = procState;app.adjTarget = s.name;if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Raise to service w/activity: "+ app);}}}}}for (int provi = app.pubProviders.size()-1;provi >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ|| schedGroup == ProcessList.SCHED_GROUP_BACKGROUND|| procState > ActivityManager.PROCESS_STATE_TOP);provi--) {ContentProviderRecord cpr = app.pubProviders.valueAt(provi);for (int i = cpr.connections.size()-1;i >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ|| schedGroup == ProcessList.SCHED_GROUP_BACKGROUND|| procState > ActivityManager.PROCESS_STATE_TOP);i--) {ContentProviderConnection conn = cpr.connections.get(i);ProcessRecord client = conn.client;if (client == app) {// Being our own client is not interesting.continue;}int clientAdj = computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now);int clientProcState = client.curProcState;if (clientProcState >= ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) {// If the other app is cached for any reason, for purposes here// we are going to consider it empty.clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;}String adjType = null;if (adj > clientAdj) {if (app.hasShownUi && app != mHomeProcess&& clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {adjType = "cch-ui-provider";} else {adj = clientAdj > ProcessList.FOREGROUND_APP_ADJ? clientAdj : ProcessList.FOREGROUND_APP_ADJ;adjType = "provider";}app.cached &= client.cached;}if (clientProcState <= ActivityManager.PROCESS_STATE_TOP) {if (clientProcState == ActivityManager.PROCESS_STATE_TOP) {// Special handling of clients who are in the top state.// We *may* want to consider this process to be in the// top state as well, but only if there is not another// reason for it to be running.  Being on the top is a// special state, meaning you are specifically running// for the current top app.  If the process is already// running in the background for some other reason, it// is more important to continue considering it to be// in the background state.mayBeTop = true;clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;mayBeTopType = adjType = "provider-top";mayBeTopSource = client;mayBeTopTarget = cpr.name;} else {// Special handling for above-top states (persistent// processes).  These should not bring the current process// into the top state, since they are not on top.  Instead// give them the best state after that.clientProcState =ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;if (adjType == null) {adjType = "provider";}}}if (procState > clientProcState) {procState = clientProcState;}if (client.curSchedGroup > schedGroup) {schedGroup = ProcessList.SCHED_GROUP_DEFAULT;}if (adjType != null) {app.adjType = adjType;app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_PROVIDER_IN_USE;app.adjSource = client;app.adjSourceProcState = clientProcState;app.adjTarget = cpr.name;if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Raise to " + adjType+ ": " + app + ", due to " + client+ " adj=" + adj + " procState=" + procState);}}// If the provider has external (non-framework) process// dependencies, ensure that its adjustment is at least// FOREGROUND_APP_ADJ.if (cpr.hasExternalProcessHandles()) {if (adj > ProcessList.FOREGROUND_APP_ADJ) {adj = ProcessList.FOREGROUND_APP_ADJ;schedGroup = ProcessList.SCHED_GROUP_DEFAULT;app.cached = false;app.adjType = "ext-provider";app.adjTarget = cpr.name;if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Raise to external provider: " + app);}if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;}}}if (app.lastProviderTime > 0 &&(app.lastProviderTime+mConstants.CONTENT_PROVIDER_RETAIN_TIME) > now) {if (adj > ProcessList.PREVIOUS_APP_ADJ) {adj = ProcessList.PREVIOUS_APP_ADJ;schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;app.cached = false;app.adjType = "recent-provider";if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Raise to recent provider: " + app);}if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;app.adjType = "recent-provider";if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Raise to recent provider: " + app);}}if (mayBeTop && procState > ActivityManager.PROCESS_STATE_TOP) {// A client of one of our services or providers is in the top state.  We// *may* want to be in the top state, but not if we are already running in// the background for some other reason.  For the decision here, we are going// to pick out a few specific states that we want to remain in when a client// is top (states that tend to be longer-term) and otherwise allow it to go// to the top state.switch (procState) {case ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE:// Something else is keeping it at this level, just leave it.break;case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND:case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND:case ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND:case ActivityManager.PROCESS_STATE_SERVICE:// These all are longer-term states, so pull them up to the top// of the background states, but not all the way to the top state.procState = ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;app.adjType = mayBeTopType;app.adjSource = mayBeTopSource;app.adjTarget = mayBeTopTarget;if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "May be top raise to " + mayBeTopType+ ": " + app + ", due to " + mayBeTopSource+ " adj=" + adj + " procState=" + procState);break;default:// Otherwise, top is a better choice, so take it.procState = ActivityManager.PROCESS_STATE_TOP;app.adjType = mayBeTopType;app.adjSource = mayBeTopSource;app.adjTarget = mayBeTopTarget;if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "May be top raise to " + mayBeTopType+ ": " + app + ", due to " + mayBeTopSource+ " adj=" + adj + " procState=" + procState);break;}}if (procState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {if (app.hasClientActivities) {// This is a cached process, but with client activities.  Mark it so.procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;app.adjType = "cch-client-act";} else if (app.treatLikeActivity) {// This is a cached process, but somebody wants us to treat it like it has// an activity, okay!procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;app.adjType = "cch-as-act";}}if (adj == ProcessList.SERVICE_ADJ) {if (doingAll) {app.serviceb = mNewNumAServiceProcs > (mNumServiceProcs/3);mNewNumServiceProcs++;//Slog.i(TAG, "ADJ " + app + " serviceb=" + app.serviceb);if (!app.serviceb) {// This service isn't far enough down on the LRU list to// normally be a B service, but if we are low on RAM and it// is large we want to force it down since we would prefer to// keep launcher over it.if (mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL&& app.lastPss >= mProcessList.getCachedRestoreThresholdKb()) {app.serviceHighRam = true;app.serviceb = true;//Slog.i(TAG, "ADJ " + app + " high ram!");} else {mNewNumAServiceProcs++;//Slog.i(TAG, "ADJ " + app + " not high ram!");}} else {app.serviceHighRam = false;}}if (app.serviceb) {adj = ProcessList.SERVICE_B_ADJ;}}//终于计算完毕app.curRawAdj = adj;//Slog.i(TAG, "OOM ADJ " + app + ": pid=" + app.pid +//      " adj=" + adj + " curAdj=" + app.curAdj + " maxAdj=" + app.maxAdj);//每个进程跟进他的类型设定了maxAdj最大值,计算出的adj不能大于最大if (adj > app.maxAdj) {adj = app.maxAdj;if (app.maxAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {schedGroup = ProcessList.SCHED_GROUP_DEFAULT;}}// Do final modification to adj.  Everything we do between here and applying// the final setAdj must be done in this function, because we will also use// it when computing the final cached adj later.  Note that we don't need to// worry about this for max adj above, since max adj will always be used to// keep it out of the cached vaues.app.curAdj = app.modifyRawOomAdj(adj);app.curSchedGroup = schedGroup;app.curProcState = procState;app.foregroundActivities = foregroundActivities;return app.curRawAdj;}

主要计算进程的 adjSeq,curRawAdj,foregroundActivities,curSchedGroup,curProcState,systemNoUi,curRawAdj

其实上面的计算思路跟很多文章讲的

Android中APP的进程层次一共5级:

  • 前台进程(Foreground process)
  • 可见进程(Visible process)
  • 服务进程(Service process)
  • 后台进程(Background process)
  • 空进程(Empty process)

ActivityManagerService之进程管理(四)相关推荐

  1. Linux内核学习008——进程管理(四)

    Linux内核学习007--进程管理(四) 进程家族树 Unix系统的进程之间存在一个明显的继承关系,所有的进程都是PID为1的init进程的后代.内核在系统启动的最后阶段启动init进程,然后ini ...

  2. android7.0 进程管理,Android 7.0 ActivityManagerService(8) 进程管理相关流程分析(2) updateOomAdjLocked...

    前一篇博客进程管理相关流程分析(1)里, 我们介绍了AMS中updateLruProcessLocked函数相关的流程. updateLruProcessLocked只是按照进程中运行的组件,粗略地定 ...

  3. 进程管理(四)——进程调度

    前言 上一篇介绍了操作系统线程的概念,接下来开始介绍具体是如何调度线程的. 进程调度实际上是调度进程中的线程,所以说线程是 CPU 调度的基本单位. 进程调度 进程调度又称 CPU 调度,指的是决定哪 ...

  4. linux设备驱动归纳总结(四):1.进程管理的相关概念【转】

    本文转载自;http://blog.chinaunix.net/uid-25014876-id-64866.html linux设备驱动归纳总结(四):1.进程管理的相关概念 xxxxxxxxxxxx ...

  5. Linux权限和进程管理、网络配置、任务调度(四)

    目录 一.组管理和权限管理 1.Linux组基本介绍 2.文件/目录所有者 (1)查看文件的所有者 (2)修改文件所有者 3.组的创建 4.文件/目录所在组 (1)查看文件/目录所在组 (2)修改文件 ...

  6. linux进程配置异常中断自动重启_Linux系统配置及服务管理 (四)进程管理

    一.进程简介 进程是已启动的可执行程序的运行实例,进程有以下组成部分: • 已分配内存的地址空间: • 安全属性,包括所有权凭据和特权: • 程序代码的一个或多个执行线程: • 进程状态. 程序: 二 ...

  7. 手机卫士09_应用程序四种查看_ListView小标题_进程管理

    手机卫士09_应用程序四种查看_ListView小标题_进程管理 1.悬浮窗体的功能实现: 1.1.应用程序的卸载: 包安装器 packageInstall,包卸载packageruninstall ...

  8. linux进程管理内存管理,Linux专业知识四:Linux系统进程管理及查看内存

    本文主讲Linux专业知识之Linux系统进程管理及查看内存的情况,以Redhat RHEL7操作系统为例. 一.进程 程序与进程:程序是静态的(文件),进程是动态的(运行的程序). 进程和线程:一个 ...

  9. 计算机进程管理与虚拟机实验答案,实验四虚拟机实验报告解读.doc

    电子科技大学 信 息 网 络 技 术 实 验 报 告 政治与公共管理学院 2016-03-17 实验名称 虚拟机上安装Linux系统并调试实验 实验编号 004 姓名 罗佳 学号 2014120101 ...

最新文章

  1. R语言构建文本分类模型并使用LIME进行模型解释实战:文本数据预处理、构建词袋模型、构建xgboost文本分类模型、基于文本训练数据以及模型构建LIME解释器解释多个测试语料的预测结果并可视化
  2. 德鲁克对管理的十大看法
  3. 学python需要什么基础知识-学Python需要什么基础知识?零基础可以学Python吗?
  4. 远哥Amoeba源码分析之:核心类说明
  5. 献给网页开发者的20款Firefox插件
  6. PHP memory_get_usage()管理内存
  7. Xcode7 无账号真机测试!!
  8. poj2481树状数组解二维偏序
  9. SQL基本操作(三):存储过程和触发器
  10. JS数据结构第六篇 --- 二叉树力扣练习题
  11. 打开与关闭Linux防火墙
  12. 加壳、脱壳以及如何病毒免杀技术与原理
  13. ACL:是什么?目的?使用场景?
  14. intel RealSense摄像头比较
  15. 正则表达式(常用正则表达式)
  16. 计算机科学期刊是b类吗,《计算机科学》体例格式
  17. latex 表格紧跟指定的文字后面
  18. PS基础入门(一.橡皮檫的介绍)
  19. 项目管理经验谈- mindjet思维导图的使用
  20. 开发平台的优势在哪?

热门文章

  1. 【Industry digitization】能源供应商的数字化转型,能源世界正在发生不可逆转的深刻变革
  2. 计算机主机内部主要由哪些硬件构成,计算机硬件由哪几部分组成?各部分的作用是什么?...
  3. 互联网告别免费时代,准备好了吗?
  4. 谷歌浏览器如何截全屏图片?
  5. 13 MATLAB判别分析
  6. 0x01.被动信息收集
  7. 【优化求解】基于猫群算法CSO求解最优目标matlab源码
  8. tcp可靠传输的机制有哪些(面试必看
  9. XStream使用教程
  10. python简单绘图教程视频_Python绘图的简单教程(I)-基本元素,python,一