Android系统的进程管理(创建->优先级->回收)

一、进程的创建

1、概述

Android系统以Linux内核为基础,所以对于进程的管理自然离不开Linux本身提供的机制。例如:

  • 通过fork来创建进行
  • 通过信号量来管理进程
  • 通过proc文件系统来查询和调整进程状态 等

对于Android来说,进程管理的主要内容包括以下几个部分内容:

  • 进程的创建
  • 进程的优先级管理
  • 进程的内存管理
  • 进程的回收和死亡处理

本文会专门讲解进程的创建,其余部分将在后面的文章中讲解。

2、主要模块

为了便于下文的讲解,这里先介绍一下Android系统中牵涉到进程创建的几个主要模块。

同时为了便于读者更详细的了解这些模块,这里也同时提供了这些模块的代码路径。

这里提到的代码路径是指AOSP的源码数中的路径。

关于如何获取AOSP源码请参见这里:Downloading the Source

本文以Android N版本的代码为示例,所用到的Source Code Tags是:android-7.0.0_r1。

相关模块

1. app_process

代码路径:frameworks/base/cmds/app_process

说明:app_process是一个可执行程序,该程序的主要作用是启动zygote和system_server进程。

2. Zygote

代码路径:frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

说明:zygote进程是所有应用进程的父进程,这是系统中一个非常重要的进程,下文我们会详细讲解。

3. ActivityManager

代码路径:frameworks/base/services/core/java/com/android/server/am/

说明:am是ActivityManager的缩写。

这个目录下的代码负责了Android全部四大组件(Activity,Service,ContentProvider,BroadcastReceiver)的管理,并且还掌控了所有应用程序进程的创建和进程的优先级管理。

因此,这个部分的内容将是本系列文章讲解的重点。

3、进程与线程

Android官方开发网站的这篇文章:Processes and Threads 非常好的介绍了Android系统中进程相关的一些基本概念和重要知识。

在阅读下文之前,请务必将这篇文章浏览一遍。

4、关于进程

在Android系统中,进程可以大致分为系统进程应用进程两大类。

系统进程是系统内置的(例如:init,zygote,system_server进程),属于操作系统必不可少的一部分。系统进程的作用在于:

  • 管理硬件设备
  • 提供访问设备的基本能力
  • 管理应用进程

应用进程是指应用程序运行的进程。这些应用程序可能是系统出厂自带的(例如Launcher,电话,短信等应用),也可能是用户自己安装的(例如:微信,支付宝等)。

系统进程的数量通常是固定的(出厂或者系统升级之后就确定了),并且系统进程通常是一直存活,常驻内存的。系统进程的异常退出将可能导致设备无法正常使用。

而应用程序和应用进程在每个人使用的设备上通常是各不一样的。如何管理好这些不确定的应用进程,就是操作系统本身要仔细考虑的内容。也是衡量一个操作系统好坏的标准之一。

在本文中,我们会介绍initzygotesystem_server三个系统进程。

除此之外,本系列文章将会把主要精力集中在讲解Android系统如何管理应用进程上

5、init进程

init进程是一切的开始,在Android系统中,所有进程的进程号都是不确定的,唯独init进程的进程号一定是1。

因为这个进程一定是系统起来的第一个进程。

并且,init进程掌控了整个系统的启动逻辑。

我们知道,Android可能运行在各种不同的平台,不同的设备上。因此,启动的逻辑是不尽相同的。 为了适应各种平台和设备的需求,init进程的初始化工作通过init.rc配置文件来管理。

你可以在AOSP源码的system/core/rootdir/路径找到这些配置文件。

配置文件的主入口文件是init.rc,这个文件会通过import引入其他几个文件。

在本文中,我们统称这些文件为init.rc。

init.rc通过Android Init Language来进行配置。 建议读者大致阅读一下其 语法说明

init.rc中配置了系统启动的时候该做哪些事情,以及启动哪些系统进程。

这其中有两个特别重要的进程就是:zygotesystem_server进程。

  • zygote的中文意思是“受精卵“。这是一个很有寓意的名称:所有的应用进程都是由zygote fork出来的子进程,因此zygote进程是所有应用进程的父进程。
  • system_server 这个进程正如其名称一样,这是一个系统服务器。Framework层的几乎所有服务都位于这个进程中。这其中就包括管理四大组件的ActivityManagerService。

6、Zygote进程

init.rc文件会根据平台不一样,选择下面几个文件中的一个来启动zygote进程:

  • init.zygote32.rc
  • init.zygote32_64.rc
  • init.zygote64.rc
  • init.zygote64_32.rc

这几个文件的内容是大致一致的,仅仅是为了不同平台服务的。这里我们以init.zygote32.rc的文件为例,来看看其中的内容:

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks

在这段配置文件中(如果你不明白这段配置的含义,请阅读一下文档:Android Init Language),启动了一个名称叫做zygote的服务进程。这个进程是通过/system/bin/app_process 这个可执行程序创建的。

并且在启动这个可执行程序的时候,传递了-Xzygote /system/bin --zygote --start-system-server class main 这些参数。

要知道这里到底做了什么,我们需要看一下app_process的源码。

app_process的源码在这个路径:frameworks/base/cmds/app_process/app_main.cpp。

这个文件的main函数的有如下代码:

int main(int argc, char* const argv[])
{
...
    while (i < argc) {
        const char* arg = argv[i++];
        if (strcmp(arg, "--zygote") == 0) {
            zygote = true;
            niceName = ZYGOTE_NICE_NAME;
        } else if (strcmp(arg, "--start-system-server") == 0) {
            startSystemServer = true;
        ...
    }
    ...
   if (!className.isEmpty()) {
        ...
    } else {
       ...

       if (startSystemServer) {
           args.add(String8("start-system-server"));
       }
    }
...
    if (zygote) {
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    } else if (className) {
        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
    } else {
        fprintf(stderr, "Error: no class name or --zygote supplied.\n");
        app_usage();
        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
        return 10;
    }
}

这里会判断,

  • 如果执行这个命令时带了--zygote参数,就会通过runtime.start启动com.android.internal.os.ZygoteInit。
  • 如果参数中带有--start-system-server参数,就会将start-system-server添加到args中。

这段代码是C++实现的。在执行这段代码的时候还没有任何Java的环境。而runtime.start就是启动Java虚拟机,并在虚拟机中启动指定的类。于是接下来的逻辑就在ZygoteInit.java中了。

这个文件的main函数主要代码如下::

public static void main(String argv[]) {
   ...

   try {
       ...

       boolean startSystemServer = false;
       String socketName = "zygote";
       String abiList = null;
       for (int i = 1; i < argv.length; i++) {
           if ("start-system-server".equals(argv[i])) {
               startSystemServer = true;
           } else if (argv[i].startsWith(ABI_LIST_ARG)) {
               ...
           }
       }
       ...
       registerZygoteSocket(socketName);
       ...
       preload();
       ...
       Zygote.nativeUnmountStorageOnInit();

       ZygoteHooks.stopZygoteNoThreadCreation();

       if (startSystemServer) {
           startSystemServer(abiList, socketName);
       }

       Log.i(TAG, "Accepting command socket connections");
       runSelectLoop(abiList);

       closeServerSocket();
   } catch (MethodAndArgsCaller caller) {
       caller.run();
   } catch (RuntimeException ex) {
       Log.e(TAG, "Zygote died with exception", ex);
       closeServerSocket();
       throw ex;
   }
}

在这段代码中,我们主要关注如下几行:

  1. 通过 registerZygoteSocket(socketName); 注册Zygote Socket
  2. 通过 preload(); 预先加载所有应用都需要的公共资源
  3. 通过 startSystemServer(abiList, socketName); 启动system_server
  4. 通过 runSelectLoop(abiList); 在Looper上等待连接

这里需要说明的是:zygote进程启动之后,会启动一个socket套接字,并通过Looper一直在这个套接字上等待连接。

所有应用进程都是通过发送数据到这个套接字上,然后由zygote进程创建的。

这里还有一点说明的是: 在Zygote进程中,会通过preload函数加载需要应用程序都需要的公共资源。

预先加载这些公共资源有如下两个好处:

  • 加快应用的启动速度 因为这些资源已经在zygote进程启动的时候加载好了
  • 通过共享的方式节省内存 这是Linux本身提供的机制:父进程已经加载的内容可以在子进程中进行共享,而不用多份数据拷贝(除非子进程对这些数据进行了修改。)

preload的资源主要是Framework相关的一些基础类和Resource资源,而这些资源正是所有应用都需要的:

开发者通过Android SDK开发应用所调用的API实现都在Framework中。

static void preload() {
   Log.d(TAG, "begin preload");
   Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "BeginIcuCachePinning");
   beginIcuCachePinning();
   Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
   Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadClasses");
   preloadClasses();
   Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
   Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadResources");
   preloadResources();
   Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
   Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadOpenGL");
   preloadOpenGL();
   Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
   preloadSharedLibraries();
   preloadTextResources();

   WebViewFactory.prepareWebViewInZygote();
   endIcuCachePinning();
   warmUpJcaProviders();
   Log.d(TAG, "end preload");
}

7、system_server进程

上文已经提到,zygote进程起来之后会根据需要启动system_server进程。

system_server进程中包含了大量的系统服务。例如:

  • 负责网络管理的NetworkManagementService
  • 负责窗口管理的WindowManagerService
  • 负责震动管理的VibratorService
  • 负责输入管理的InputManagerService

等等。关于system_server,我们今后会其他的文章中专门讲解,这里不做过多说明。

在本文中,我们只关注system_server中的ActivityManagerService这个系统服务。

8、ActivityManagerService

上文中提到:zygote进程在启动之后会启动一个socket,然后一直在这个socket等待连接。

而会连接它的就是ActivityManagerService。

因为ActivityManagerService掌控了所有应用进程的创建。

所有应用程序的进程都是由ActivityManagerService通过socket发送请求给Zygote进程,然后由zygote fork创建的。

ActivityManagerService通过Process.start方法来请求zygote创建进程:

public static final ProcessStartResult start(final String processClass,
                             final String niceName,
                             int uid, int gid, int[] gids,
                             int debugFlags, int mountExternal,
                             int targetSdkVersion,
                             String seInfo,
                             String abi,
                             String instructionSet,
                             String appDataDir,
                             String[] zygoteArgs) {
   try {
       return startViaZygote(processClass, niceName, uid, gid, gids,
               debugFlags, mountExternal, targetSdkVersion, seInfo,
               abi, instructionSet, appDataDir, zygoteArgs);
   } catch (ZygoteStartFailedEx ex) {
       Log.e(LOG_TAG,
               "Starting VM process through Zygote failed");
       throw new RuntimeException(
               "Starting VM process through Zygote failed", ex);
   }
}

这个函数会将启动进程所需要的参数组装好,并通过socket发送给zygote进程。然后zygote进程根据发送过来的参数将进程fork出来。

在ActivityManagerService中,调用Process.start的地方是下面这个方法:

private final void startProcessLocked(ProcessRecord app, String hostingType,
       String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {

...
  Process.ProcessStartResult startResult = Process.start(entryPoint,
          app.processName, uid, uid, gids, debugFlags, mountExternal,
          app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
          app.info.dataDir, entryPointArgs);
...
}

下文中我们会看到,所有四大组件进程的创建,都是调用这里的startProcessLocked这个方法而创建的。

对于每一个应用进程,在ActivityManagerService中,都有一个ProcessRecord与之对应。这个对象记录了应用进程的所有详细状态。

PS:对于ProcessRecord的内部结构,在下一篇文章中,我们会讲解。

为了查找方便,对于每个ProcessRecord会存在下面两个集合中。

  • 按名称和uid组织的集合
/**
* All of the applications we currently have running organized by name.
* The keys are strings of the application package name (as
* returned by the package manager), and the keys are ApplicationRecord
* objects.
*/
final ProcessMap<ProcessRecord> mProcessNames = new ProcessMap<ProcessRecord>();
  • 按pid组织的集合:
/**
* All of the processes we currently have running organized by pid.
* The keys are the pid running the application.
*
* <p>NOTE: This object is protected by its own lock, NOT the global
* activity manager lock!
*/
final SparseArray<ProcessRecord> mPidsSelfLocked = new SparseArray<ProcessRecord>();

下面这幅图小节了上文的这些内容:

9、关于应用组件

Processes and Threads 提到:

当某个应用组件启动且该应用没有运行其他任何组件时,Android 系统会使用单个执行线程为应用启动新的 Linux 进程。

因此,四大组件中的任何一个先起来都会导致应用进程的创建。下文我们就详细看一下,它们启动时,各自是如何导致应用进程的创建的。

PS:四大组件的管理本身又是一个比较大的话题,限于篇幅关系,这里不会非常深入的讲解,这里主要是讲解四大组件与进程创建的关系。

在应用程序中,开发者通过:

  • startActivity(Intent intent) 来启动Activity
  • startService(Intent service) 来启动Service
  • sendBroadcast(Intent intent) 来发送广播
  • ContentResolver 中的接口来使用ContentProvider

这其中,startActivity,startService和sendBroadcast还有一些重载方法。

其实这里提到的所有这些方法,最终都是通过Binder调用到ActivityManagerService中,由其进行处理的。

这里特别说明一下:应用进程和ActivityManagerService所在进程(即system_server进程)是相互独立的,两个进程之间的方法通常是不能直接互相调用的。

而Android系统中,专门提供了Binder框架来提供进程间通讯和方法调用的能力。

调用关系如下图所示:

10、Activity与进程创建

在ActivityManagerService中,对每一个运行中的Activity都有一个ActivityRecord对象与之对应,这个对象记录Activity的详细状态。

ActivityManagerService中的startActivity方法接受Context.startActivity的请求,该方法代码如下:

@Override
public final int startActivity(IApplicationThread caller, String callingPackage,
       Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
       int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
   return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
           resultWho, requestCode, startFlags, profilerInfo, bOptions,
           UserHandle.getCallingUserId());
}

Activity的启动是一个非常复杂的过程。这里我们简单介绍一下背景知识:

  • ActivityManagerService中通过Stack和Task来管理Activity
  • 每一个Activity都属于一个Task,一个Task可能包含多个Activity。一个Stack包含多个Task
  • ActivityStackSupervisor类负责管理所有的Stack
  • Activity的启动过程会牵涉到:
    • Intent的解析
    • Stack,Task的查询或创建
    • Activity进程的创建
    • Activity窗口的创建
    • Activity的生命周期调度

Activity的管理结构如下图所示:

在Activity启动的最后,会将前一个Activity pause,将新启动的Activity resume以便被用户看到。

在这个时候,如果发现新启动的Activity进程还没有启动,则会通过startSpecificActivityLocked将其启动。整个调用流程如下:

  • ActivityManagerService.activityPaused =>
  • ActivityStack.activityPausedLocked =>
  • ActivityStack.completePauseLocked =>
  • ActivityStackSupervisor.ensureActivitiesVisibleLocked =>
  • ActivityStack.makeVisibleAndRestartIfNeeded =>
  • ActivityStackSupervisor.startSpecificActivityLocked =>
  • ActivityManagerService.startProcessLocked

ActivityStackSupervisor.startSpecificActivityLocked 关键代码如下:

void startSpecificActivityLocked(ActivityRecord r,
       boolean andResume, boolean checkConfig) {
   // Is this activity's application already running?
   ProcessRecord app = mService.getProcessRecordLocked(r.processName,
           r.info.applicationInfo.uid, true);

   r.task.stack.setLaunchTime(r);

   if (app != null && app.thread != null) {
       ...
   }

   mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
           "activity", r.intent.getComponent(), false, false, true);
}

这里的ProcessRecord app 描述了Activity所在进程。

11、Service与进程创建

Service的启动相对于Activity来说要简单一些。

在ActivityManagerService中,对每一个运行中的Service都有一个ServiceRecord对象与之对应,这个对象记录Service的详细状态。

ActivityManagerService中的startService方法处理Context.startServiceAPI的请求,相关代码:

@Override
public ComponentName startService(IApplicationThread caller, Intent service,
       String resolvedType, String callingPackage, int userId)
       throws TransactionTooLargeException {
   ...
   synchronized(this) {
       final int callingPid = Binder.getCallingPid();
       final int callingUid = Binder.getCallingUid();
       final long origId = Binder.clearCallingIdentity();
       ComponentName res = mServices.startServiceLocked(caller, service,
               resolvedType, callingPid, callingUid, callingPackage, userId);
       Binder.restoreCallingIdentity(origId);
       return res;
   }
}

这段代码中的mServices对象是ActiveServices类型的,这个类专门负责管理活动的Service。

启动Service的调用流程如下:

  • ActivityManagerService.startService =>
  • ActiveServices.startServiceLocked =>
  • ActiveServices.startServiceInnerLocked =>
  • ActiveServices.bringUpServiceLocked =>
  • ActivityManagerService.startProcessLocked

ActiveServices.bringUpServiceLocked会判断如果Service所在进程还没有启动,

则通过ActivityManagerService.startProcessLocked将其启动。相关代码如下:

// Not running -- get it started, and enqueue this service record
// to be executed when the app comes up.
if (app == null && !permissionsReviewRequired) {
  if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
          "service", r.name, false, isolated, false)) == null) {
      String msg = "Unable to launch app "
              + r.appInfo.packageName + "/"
              + r.appInfo.uid + " for service "
              + r.intent.getIntent() + ": process is bad";
      Slog.w(TAG, msg);
      bringDownServiceLocked(r);
      return msg;
  }
  if (isolated) {
      r.isolatedProc = app;
  }
}

这里的mAm 就是ActivityManagerService。

12、Provider与进程创建

在ActivityManagerService中,对每一个运行中的ContentProvider都有一个ContentProviderRecord对象与之对应,这个对象记录ContentProvider的详细状态。

开发者通过ContentResolver中的insert, delete, update, query这些API来使用ContentProvider。在ContentResolver的实现中,无论使用这里的哪个接口,ContentResolver都会先通过acquireProvider 这个方法来获取到一个类型为IContentProvider的远程接口。这个远程接口对接了ContentProvider的实现提供方。

同一个ContentProvider可能同时被多个模块使用,而调用ContentResolver接口的进程只是ContentProvider的一个客户端而已,真正的ContentProvider提供方是运行自身的进程中的,两个进程的通讯需要通过Binder的远程接口形式来调用。如下图所示:

ContentResolver.acquireProvider 最终会调用到ActivityManagerService.getContentProvider中,该方法代码如下:

@Override
public final ContentProviderHolder getContentProvider(
       IApplicationThread caller, String name, int userId, boolean stable) {
   enforceNotIsolatedCaller("getContentProvider");
   if (caller == null) {
       String msg = "null IApplicationThread when getting content provider "
               + name;
       Slog.w(TAG, msg);
       throw new SecurityException(msg);
   }
   // The incoming user check is now handled in checkContentProviderPermissionLocked() to deal
   // with cross-user grant.
   return getContentProviderImpl(caller, name, null, stable, userId);
}

而在getContentProviderImpl这个方法中,会判断对应的ContentProvider进程有没有启动,

如果没有,则通过startProcessLocked方法将其启动。

13、Receiver与进程创建

开发者通过Context.sendBroadcast接口来发送广播。ActivityManagerService.broadcastIntent 方法了对应广播发送的处理。

广播是一种一对多的消息形式,广播接受者的数量是不确定的。因此发送广播本身可能是一个很耗时的过程(因为要逐个通知)。

在ActivityManagerService内部,是通过队列的形式来管理广播的:

  • BroadcastQueue 描述了一个广播队列
  • BroadcastRecord 描述了一个广播事件

在ActivityManagerService中,如果收到了一个发送广播的请求,会先创建一个BroadcastRecord接着将其放入BroadcastQueue中。

然后通知队列自己去处理这个广播。然后ActivityManagerService自己就可以继续处理其他请求了。

广播队列本身是在另外一个线程处理广播的发送的,这样保证的ActivityManagerService主线程的负载不会太重。

在BroadcastQueue.processNextBroadcast(boolean fromMsg) 方法中真正实现了通知广播事件到接受者的逻辑。在这个方法,如果发现接受者(即BrodcastReceiver)还没有启动,便会通过ActivityManagerService.startProcessLocked 方法将其启动。相关如下所示:

final void processNextBroadcast(boolean fromMsg) {
    ...
       // Hard case: need to instantiate the receiver, possibly
       // starting its application process to host it.

       ResolveInfo info =
           (ResolveInfo)nextReceiver;
       ComponentName component = new ComponentName(
               info.activityInfo.applicationInfo.packageName,
               info.activityInfo.name);
    ...
       // Not running -- get it started, to be executed when the app comes up.
       if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
               "Need to start app ["
               + mQueueName + "] " + targetProcess + " for broadcast " + r);
       if ((r.curApp=mService.startProcessLocked(targetProcess,
               info.activityInfo.applicationInfo, true,
               r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
               "broadcast", r.curComponent,
               (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
                       == null) {
           // Ah, this recipient is unavailable.  Finish it if necessary,
           // and mark the broadcast record as ready for the next.
           Slog.w(TAG, "Unable to launch app "
                   + info.activityInfo.applicationInfo.packageName + "/"
                   + info.activityInfo.applicationInfo.uid + " for broadcast "
                   + r.intent + ": process is bad");
           logBroadcastReceiverDiscardLocked(r);
           finishReceiverLocked(r, r.resultCode, r.resultData,
                   r.resultExtras, r.resultAbort, false);
           scheduleBroadcastsLocked();
           r.state = BroadcastRecord.IDLE;
           return;
       }

       mPendingBroadcast = r;
       mPendingBroadcastRecvIndex = recIdx;
   }
}

至此,四大组件的启动就已经分析完了。

二、进程的优先级

1、前言

进程的优先级反应了系统对于进程重要性的判定。

在Android系统中,进程的优先级影响着以下三个因素:

  • 当内存紧张时,系统对于进程的回收策略
  • 系统对于进程的CPU调度策略
  • 虚拟机对于进程的内存分配和垃圾回收策略

本文会主要讲解系统对于进程优先级的判断依据和计算方法。

Processes and Threads (如果你还没有阅读,请立即阅读一下这篇文章)一文中,我们已经了解到,系统对于进程的优先级有如下五个分类:

  1. 前台进程
  2. 可见进程
  3. 服务进程
  4. 后台进程
  5. 空进程

实际上这只是一个粗略的划分。在系统的内部实现中,优先级远不止这么五种。

2、优先级的依据

我们来简单列一下应用组件与进程的相关信息:

  • 每一个Android的应用进程中,都可能包含四大组件中的一个/种或者多个/种。
  • 对于运行中的Service和ContentProvider来说,可能有若干个客户端进程正在对其使用。
  • 应用进程是由ActivityManagerService发送请求让zygote创建的,并且ActivityManagerService中对于每一个运行中的进程都有一个ProcessRecord对象与之对应。

ProcessRecord简化图如下所示:

在ProcessRecord中,详细记录了应用组件的相关信息,相关代码如下:

// all activities running in the process
final ArrayList<ActivityRecord> activities = new ArrayList<>();
// all ServiceRecord running in this process
final 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 holds
final ArraySet<ConnectionRecord> connections = new ArraySet<>();
// all IIntentReceivers that are registered from this process.
final ArraySet<ReceiverList> receivers = new ArraySet<>();
// class (String) -> ContentProviderRecord
final ArrayMap<String, ContentProviderRecord> pubProviders = new ArrayMap<>();
// All ContentProviderRecord process is using
final ArrayList<ContentProviderConnection> conProviders = new ArrayList<>();

这里的:

  • activities 记录了进程中运行的Activity
  • services,executingServices 记录了进程中运行的Service
  • receivers 记录了进程中运行的BroadcastReceiver
  • pubProviders 记录了进程中运行的ContentProvider

而:

  • connections 记录了对于Service连接
  • conProviders 记录了对于ContentProvider的连接

连接就是对于客户端使用状态的记录,对于Service和ContentProvider是类似的,每有一个客户端就需要记录一个连接。连接的意义在于:连接的客户端的进程优先级会影响被使用的Service和ContentProvider所在进程的优先级。 例如:当一个后台的Service正在被一个前台的Activity使用,那么这个后台的Service就需要设置一个较高的优先级以便不会被回收。(否则后台Service进程一旦被回收,便会对前台的Activity造成影响。)

所有这些组件的状态就是其所在进程优先级的决定性因素。 组件的状态是指:

  • Activity是否在前台,用户是否可见
  • Service正在被哪些客户端使用
  • ContentProvider正在被哪些客户端使用
  • BroadcastReceiver是否正在接受广播

3、优先级的基础

3.1 oom_score_adj

对于每一个运行中的进程,Linux内核都通过proc文件系统暴露这样一个文件来允许其他程序修改指定进程的优先级:

/proc/[pid]/oom_score_adj。(修改这个文件需要root权限)

这个文件允许的值的范围是:-1000 ~ +1000之间。值越小,表示进程越重要

当内存非常紧张时,系统便会遍历所有进程,以确定哪个进程需要被杀死以回收内存,此时便会读取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这个文件。

ProcessRecord中下面这些属性反应了oom_score_adj的值:

int maxAdj;                 // Maximum OOM adjustment for this process
int curRawAdj;              // Current OOM unlimited adjustment for this process
int setRawAdj;              // Last set OOM unlimited adjustment for this process
int curAdj;                 // Current OOM adjustment for this process
int setAdj;                 // Last set OOM adjustment for this process

maxAdj 指定了该进程允许的oom_score_adj最大值。这个属性主要是给系统应用和常驻内存的进程使用,这些进程的优先级的计算方法与应用进程的计算方法不一样,通过设定maxAdj保证这些进程一直拥有较高的优先级(在后面”优先级的算法“中,我们会看到对于这个属性的使用)。

除此之外,还有四个属性。

这其中,curXXX这一组记录了这一次优先级计算的结果。在计算完成之后,会将curXXX复制给对应的setXXX这一组上进行备份。 (下文的其他属性也会看到curXXX和setXXX的形式,和这里的原理是一样的。)

另外,xxxRawAdj记录了没有经过限制的adj值,“没有经过限制”是指这其中的值可能是超过了oom_score_adj文件所允许的范围(-1000 ~ 1000)。

为了便于管理,ProcessList.java中预定义了oom_score_adj的可能取值。

其实这里的预定义值也是对应用进程的一种分类,它们是:

static final int UNKNOWN_ADJ = 1001; // 未知进程
static final int PREVIOUS_APP_ADJ = 700; // 前一个应用
static final int HOME_APP_ADJ = 600; // 桌面进程
static final int SERVICE_ADJ = 500; // 包含了Service的进程
static final int HEAVY_WEIGHT_APP_ADJ = 400; // 重量级进程
static final int BACKUP_APP_ADJ = 300; // 备份应用进程
static final int PERCEPTIBLE_APP_ADJ = 200; // 可感知的进程
static final int VISIBLE_APP_ADJ = 100; // 可见进程
static final int VISIBLE_APP_LAYER_MAX = PERCEPTIBLE_APP_ADJ - VISIBLE_APP_ADJ - 1;
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; // 系统进程
static final int NATIVE_ADJ = -1000; // native系统进程

这里我们看到,FOREGROUND_APP_ADJ = 0,这个是前台应用进程的优先级。这是用户正在交互的应用,它们是很重要的,系统不应当把它们回收了。

FOREGROUND_APP_ADJ = 0是普通应用程序能够获取到的最高优先级。

而VISIBLE_APP_ADJ,PERCEPTIBLE_APP_ADJ,PREVIOUS_APP_ADJ这几个级别的优先级就逐步降低了。

VISIBLE_APP_ADJ是具有可见Activity进程的优先级:同一时刻,不一定只有一个Activity是可见的,如果前台Activity设置了透明属性,那么背后的Activity也是可见的。

PERCEPTIBLE_APP_ADJ是指用户可感知的进程,可感知的进程包括:

  • 进程中包含了处于pause状态或者正在pause的Activity
  • 进程中包含了正在stop的Activity
  • 进程中包含了前台的Service

另外,PREVIOUS_APP_ADJ描述的是前一个应用的优先级。所谓“前一个应用”是指:在启动新的Activity时,如果新启动的Activity是属于一个新的进程的,那么当前即将被stop的Activity所在的进程便会成为“前一个应用”进程。

而HEAVY_WEIGHT_APP_ADJ 描述的重量级进程是指那些通过Manifest指明不能保存状态的应用进程。

除此之外,Android系统中,有一些系统应用会常驻内存,这些应用通常是系统实现的一部分,如果它们不存在,系统将处于比较奇怪的状态,例如SystemUI(状态栏,Keyguard都处于这个应用中)。

所以它们的优先级比所有应用进程的优先级更高:PERSISTENT_SERVICE_ADJ = -700,PERSISTENT_PROC_ADJ = -800。

另外,还有一些系统服务的实现,如果这些系统服务不存在,系统将无法工作,所以这些应用的优先级最高,几乎是任何任何时候都需要存在的:SYSTEM_ADJ = -900,NATIVE_ADJ = -1000。

3.2 Schedule Group

内核负责了进程的CPU调度,所有运行中的进程并非能平等的能获取相等的时间片。在ProcessRecord中,通过Schedule Group来记录进程的调度组:

int curSchedGroup;          // Currently desired scheduling class
int setSchedGroup;          // Last set to background scheduling class

它们可能的取值定义在ProcessList.java中:

// Activity manager's version of Process.THREAD_GROUP_BG_NONINTERACTIVE
static final int SCHED_GROUP_BACKGROUND = 0;
// Activity manager's version of Process.THREAD_GROUP_DEFAULT
static final int SCHED_GROUP_DEFAULT = 1;
// Activity manager's version of Process.THREAD_GROUP_TOP_APP
static final int SCHED_GROUP_TOP_APP = 2;
// 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 = 3;

3.3 Process State

进程的状态会影响虚拟机对于进程的内存分配和垃圾回收策略,ProcessRecord中的下面这几个属性记录了进程的状态:

int curProcState; // Currently computed process state
int repProcState; // Last reported process state
int setProcState; // Last set process state in process tracker
int pssProcState; // Currently requesting pss for

这些属性可能的取值定义在ActivityManager中,这些常量的名称已经说明了其作用:

public static final int PROCESS_STATE_NONEXISTENT = -1;

public static final int PROCESS_STATE_PERSISTENT = 0;

public static final int PROCESS_STATE_PERSISTENT_UI = 1;

public static final int PROCESS_STATE_TOP = 2;

public static final int PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 3;

public static final int PROCESS_STATE_FOREGROUND_SERVICE = 4;

public static final int PROCESS_STATE_TOP_SLEEPING = 5;

public static final int PROCESS_STATE_IMPORTANT_FOREGROUND = 6;

public static final int PROCESS_STATE_IMPORTANT_BACKGROUND = 7;

public static final int PROCESS_STATE_BACKUP = 8;

public static final int PROCESS_STATE_HEAVY_WEIGHT = 9;

public static final int PROCESS_STATE_SERVICE = 10;

public static final int PROCESS_STATE_RECEIVER = 11;

public static final int PROCESS_STATE_HOME = 12;

public static final int PROCESS_STATE_LAST_ACTIVITY = 13;

public static final int PROCESS_STATE_CACHED_ACTIVITY = 14;

public static final int PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 15;

public static final int PROCESS_STATE_CACHED_EMPTY = 16;

4、优先级的更新

前文已经提到,系统会对处于不同状态的进程设置不同的优先级。但实际上,进程的状态是一直在变化中的。例如:用户可以随时会启动一个新的Activity,或者将一个前台的Activity切换到后台。在这个时候,发生状态变化的Activity的所在进程的优先级就需要进行更新。

并且,Activity可能会使用其他的Service或者ContentProvider。当Activity的进程优先级发生变化的时候,它所使用的Service或者ContentProvider的优先级也应当发生变化。

ActivityManagerService中有如下两个方法用来更新进程的优先级:

  • final boolean updateOomAdjLocked(ProcessRecord app)
  • final void updateOomAdjLocked()

第一个方法是针对指定的单个进程更新优先级。第二个是对所有进程更新优先级。

在下面的这些情况下,需要对指定的应用进程更新优先级:

  • 当有一个新的进程开始使用本进程中的ContentProvider
  • 当本进程中的一个Service被其他进程bind或者unbind
  • 当本进程中的Service的执行完成或者退出了
  • 当本进程中一个BroadcastReceiver正在接受广播
  • 当本进程中的BackUpAgent启动或者退出了

final boolean updateOomAdjLocked(ProcessRecord app) 被调用的关系如下图所示:

在有些情况下,系统需要对所有应用进程的优先级进行更新,譬如:

  • 当有一个新的进程启动时
  • 当有一个进程退出时
  • 当系统在清理后台进程时
  • 当有一个进程被标记为前台进程时
  • 当有一个进程进入或者退出cached状态时
  • 当系统锁屏或者解锁时
  • 当有一个Activity启动或者退出时
  • 当系统正在处理一个广播事件时
  • 当前台Activity发生改变时
  • 当有一个Service启动时

final void updateOomAdjLocked() 被调用的关系图如下所示:

5、优先级的算法

ActivityManagerService中的computeOomAdjLocked方法负责计算进程的优先级,这个方法总计约700行,执行流程主要包含如下10个步骤:

下面我们来详细看其中的每一个步骤:

  • 1.确认该进程是否是空进程空进程中没有任何组件,因此主线程也为null(ProcessRecord.thread描述了应用进程的主线程)。如果是空进程,则不需要再做后面的计算了。直接设置为ProcessList.CACHED_APP_MAX_ADJ级别即可。
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);
}
  • 2.确认是否设置了maxAdj上文已经提到过,系统进程或者Persistent进程会通过设置maxAdj来保持其较高的优先级,对于这类进程不用按照普通进程的算法进行计算,直接按照maxAdj的值设置即可。
if (app.maxAdj <= ProcessList.FOREGROUND_APP_ADJ) {
      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;
      app.systemNoUi = true;
      if (app == TOP_APP) {
          app.systemNoUi = false;
          app.curSchedGroup = ProcessList.SCHED_GROUP_TOP_APP;
          app.adjType = "pers-top-activity";
      } 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);
  }
  • 3.确认进程中是否有前台优先级的组件前台优先级的组件是指:a.前台的Activity; b.正在接受广播的Receiver; c.正在执行任务的Service;注:除此之外,还有Instrumentation被认为是具有较高优先级的。Instrumentation应用是辅助测试用的,正常运行的系统中不用考虑这种应用。假设进程中包含了以上提到的前台优先级的任何一个组件,则直接设置进程优先级为FOREGROUND_APP_ADJ即可。因为这已经是应用程序能够获取的最高优先级了。
int adj;
  int schedGroup;
  int procState;
  boolean foregroundActivities = false;
  BroadcastQueue queue;
  if (app == TOP_APP) {
      adj = ProcessList.FOREGROUND_APP_ADJ;
      schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
      app.adjType = "top-activity";
      foregroundActivities = true;
      procState = PROCESS_STATE_CUR_TOP;
  } else if (app.instrumentationClass != null) {
      adj = ProcessList.FOREGROUND_APP_ADJ;
      schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
      app.adjType = "instrumentation";
      procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
  } else if ((queue = isReceivingBroadcast(app)) != null) {
      adj = ProcessList.FOREGROUND_APP_ADJ;
      schedGroup = (queue == mFgBroadcastQueue)
              ? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
      app.adjType = "broadcast";
      procState = ActivityManager.PROCESS_STATE_RECEIVER;
  } else if (app.executingServices.size() > 0) {
      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;
  } else {
      schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
      adj = cachedAdj;
      procState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
      app.cached = true;
      app.empty = true;
      app.adjType = "cch-empty";
  }
  • 4.确认进程中是否有较高优先级的Activity这里需要遍历进程中的所有Activity,找出其中优先级最高的设置为进程的优先级。即便Activity不是前台Activity,但是处于下面这些状态的Activity优先级也是被认为是较高优先级的:
    1. 该Activity处于可见状态
    2. 该Activity处于Pause正在Pause状态
    3. 该Activity正在stop
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 sane
             r.app = app;
         } else {
             continue;
         }
     }
     if (r.visible) {
         // App has a visible activity; only upgrade adjustment.
         if (adj > ProcessList.VISIBLE_APP_ADJ) {
             adj = ProcessList.VISIBLE_APP_ADJ;
             app.adjType = "visible";
         }
         if (procState > PROCESS_STATE_CUR_TOP) {
             procState = PROCESS_STATE_CUR_TOP;
         }
         schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
         app.cached = false;
         app.empty = false;
         foregroundActivities = true;
         if (r.task != null && minLayer > 0) {
             final int layer = r.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 = "pausing";
         }
         if (procState > PROCESS_STATE_CUR_TOP) {
             procState = PROCESS_STATE_CUR_TOP;
         }
         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 = "stopping";
         }
         if (!r.finishing) {
             if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
                 procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
             }
         }
         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 (adj == ProcessList.VISIBLE_APP_ADJ) {
     adj += minLayer;
 }
}
  • 5.确认进程中是否有前台Service通过startForeground启动的Service被认为是前台Service。给予这类进程PERCEPTIBLE_APP_ADJ级别的优先级。
if (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;
 } else if (app.forcingToForeground != null) {
     // The user is aware of this app, so make it visible.
     adj = ProcessList.PERCEPTIBLE_APP_ADJ;
     procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
     app.cached = false;
     app.adjType = "force-fg";
     app.adjSource = app.forcingToForeground;
     schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
 }
}
  • 6.确认是否是特殊类型进程特殊类型的进程包括:重量级进程,桌面进程,前一个应用进程,正在执行备份的进程。 “重量级进程”和“前一个应用”进程在上文中已经说过了。而桌面就是指Android上的Launcher。
if (app == mHeavyWeightProcess) {
 if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ) {
     adj = ProcessList.HEAVY_WEIGHT_APP_ADJ;
     schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
     app.cached = false;
     app.adjType = "heavy";
 }
 if (procState > ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {
     procState = ActivityManager.PROCESS_STATE_HEAVY_WEIGHT;
 }
}

if (app == mHomeProcess) {
 if (adj > ProcessList.HOME_APP_ADJ) {
     adj = ProcessList.HOME_APP_ADJ;
     schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
     app.cached = false;
     app.adjType = "home";
 }
 if (procState > ActivityManager.PROCESS_STATE_HOME) {
     procState = ActivityManager.PROCESS_STATE_HOME;
 }
}

if (app == mPreviousProcess && app.activities.size() > 0) {
 if (adj > ProcessList.PREVIOUS_APP_ADJ) {
     adj = ProcessList.PREVIOUS_APP_ADJ;
     schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
     app.cached = false;
     app.adjType = "previous";
 }
 if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
     procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
 }
}

if (false) Slog.i(TAG, "OOM " + app + ": initial adj=" + adj
     + " reason=" + app.adjType);

app.adjSeq = mAdjSeq;
app.curRawAdj = adj;
app.hasStartedServices = false;

if (mBackupTarget != null && app == mBackupTarget.app) {
 if (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_IMPORTANT_BACKGROUND) {
         procState = ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
     }
     app.adjType = "backup";
     app.cached = false;
 }
 if (procState > ActivityManager.PROCESS_STATE_BACKUP) {
     procState = ActivityManager.PROCESS_STATE_BACKUP;
 }
}
  • 7.根据所有Service的客户端计算优先级这里需要遍历所有的Service,并且还需要遍历每一个Service的所有连接。然后根据连接的关系确认客户端进程的优先级来确定当前进程的优先级。ConnectionRecord.binding.client即为客户端进程ProcessRecord,由此便可以知道客户端进程的优先级。
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;
     }
     if (app.hasShownUi && app != mHomeProcess) {
         if (adj > ProcessList.SERVICE_ADJ) {
             app.adjType = "cch-started-ui-services";
         }
     } else {
         if (now < (s.lastActivity + ActiveServices.MAX_SERVICE_INACTIVITY)) {
             if (adj > ProcessList.SERVICE_ADJ) {
                 adj = ProcessList.SERVICE_ADJ;
                 app.adjType = "started-services";
                 app.cached = false;
             }
         }
         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);
  • 8.根据所有Provider的客户端确认优先级这里与Service类似,需要遍历所有的Provider,以及每一个Provider的所有连接。然后根据连接的关系确认客户端进程的优先级来确定当前进程的优先级。类似的,ContentProviderConnection.client为客户端进程的ProcessRecord。
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);
     ...
  • 9.收尾工作 收尾工作主要是根据进程中的Service,Provider的一些特殊状态做一些处理,另外还有针对空进程以及设置了maxAdj的进程做一些处理,这里就不贴出代码了。

这里想专门说明一下的是,在这一步还会对Service进程做ServiceB的区分。系统将Service进程分为ServiceA和ServiceB。ServiceA是相对来说较新的Service,而ServiceB相对来说是比较“老旧”的,对用户来说可能是不那么感兴趣的,因此ServiceB的优先级会相对低一些。

static final int SERVICE_B_ADJ = 800;
static final int SERVICE_ADJ = 500;

而ServiceB的标准是:app.serviceb = mNewNumAServiceProcs > (mNumServiceProcs/3); 即:所有Service进程的前1/3为ServiceA,剩下为ServiceB。

if (adj == ProcessList.SERVICE_ADJ) {
  if (doingAll) {
      app.serviceb = mNewNumAServiceProcs > (mNumServiceProcs/3);
      mNewNumServiceProcs++;
      if (!app.serviceb) {
          if (mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL
                  && app.lastPss >= mProcessList.getCachedRestoreThresholdKb()) {
              app.serviceHighRam = true;
              app.serviceb = true;
          } else {
              mNewNumAServiceProcs++;
          }
      } else {
          app.serviceHighRam = false;
      }
  }
  if (app.serviceb) {
      adj = ProcessList.SERVICE_B_ADJ;
  }
}

app.curRawAdj = adj;
  • 10.保存结果 最终需要把本次的计算结果保存到ProcessRecord中:
app.curAdj = app.modifyRawOomAdj(adj);
app.curSchedGroup = schedGroup;
app.curProcState = procState;
app.foregroundActivities = foregroundActivities;

6、优先级的生效

优先级的生效是指:将计算出来的优先级真正应用到系统中,applyOomAdjLocked 方法负责了此项工作。

前文中我们提到,优先级意味着三个方面,这里的生效就对应了这三个方面:

  1. ProcessList.setOomAdj(app.pid, app.info.uid, app.curAdj); 将计算出来的adj值写入到procfs中,即:/proc/[pid]/oom_score_adj 这个文件中。
  2. Process.setProcessGroup(app.pid, processGroup); 用来设置进程的调度组。
  3. app.thread.setProcessState(app.repProcState); 这个方法会最终调用到 VMRuntime.getRuntime().updateProcessState();将进程的状态设置到虚拟机中。

7、结束语

前言中我们提到,“优先级反应了系统对于进程重要性的判定。”

那么,系统如何评价进程的优先级,便是系统本身一个很重要的特性。了解系统的这一特性对于我们开发应用程序,以及对于应用程序运行的行为分析是很有意义的。

系统在判定优先级的时候,应当做到公平公正,并且不能让开发者有机可乘。

“公平公正”是指系统需要站在一个中间人的状态下,不偏倚任何一个应用,公正的将系统资源分配给真正需要的进程。并且在系统资源紧张的时候,回收不重要的进程。

通过上文的分析,我们看到,Android系统认为“重要”的进程主要有三类:

  1. 系统进程
  2. 前台与用户交互的进程
  3. 前台进程所使用到的进程

不过对于这一点是有改进的空间的,例如,可以引入用户习惯的分析:如果是用户频繁使用的应用,可以给予这些应用更高的优先级以提升这些应用的响应速度。目前,国内一些Android定制厂商已经开始做这类功能的支持。

“不能让开发者有机可乘”是指:系统对于进程优先级的判定的因素应当是不能被开发者利用的。因为一旦开发者可以利用,每个开发者都肯定会将自己的设置为高优先级,来抢占更多的资源。

需要说明的是,Android在这个方面是存在缺陷的:在Android系统上,可以通过startForeground拿到前台的优先级的。后来Google也意识到这个问题,于是在API Level 18以上的版本上,调用startForeground这个API会在通知栏显示一条通知以告知用户。但是,这个改进是有Bug的:开发者可以同时通过startForeground启动两个Service,指定同样的通知id,然后退出其中一个,这样应用的不会在通知栏显示通知图标,并且拿到了前台的优先级。这个便是让开发者“有机可乘”了。

三、内存的回收

1、前言

内存是系统中非常宝贵的资源,即便如今的移动设备上,内存已经达到4G甚至6G的级别,但对于内存的回收也依然重要,因为在Android系统上,同时运行的进程有可能会有几十甚至上百个之多。

如何将系统内存合理的分配给每个进程,以及如何进行内存回收,便是操作系统需要处理的问题之一。

本文会讲解Android系统中内存回收相关的知识。

对于内存回收,主要可以分为两个层次:

  • 进程内的内存回收:通过释放进程中的资源进行内存回收
  • 进程级的内存回收:通过杀死进程来进行内存回收

这其中,进程内的内存回收主要分为两个方面:

  • 虚拟机自身的垃圾回收机制
  • 在系统内存状态发生变化时,通知应用程序,让开发者进行内存回收

进程级的内存回收主要是依靠系统中的两个模块,它们是:

  • Linux OOM Killer
  • LowMemoryKiller

在特定场景下,他们都会通过杀死进程来进行内存回收。

下图描述了几种内存回收机制:

2、Android系统的内存管理简介

在Android系统中,进程可以大致分为系统进程应用进程两大类。

系统进程是系统内置的(例如:init,zygote,system_server进程),属于操作系统必不可少的一部分。系统进程的作用在于:

  • 管理硬件设备
  • 提供访问设备的基本能力
  • 管理应用进程

应用进程是指应用程序运行的进程。这些应用程序可能是系统出厂自带的(例如Launcher,电话,短信等应用),也可能是用户自己安装的(例如:微信,支付宝等)。

Android中应用进程通常都运行在Java虚拟机中。在Android 5.0之前的版本,这个虚拟机是Dalvik,5.0及之后版本,Android引入了新的虚拟机,称作Android Runtime,简称“ART”。

关于ART和Dalvik可以参见这里:ART and Dalvik。无论是Dalvik还是ART,本身都具有垃圾回收的能力,关于这一点,我们在后面专门讲解。

Android的应用程序都会依赖一些公共的资源,例如:Android SDK提供的类和接口,以及Framework公开的图片,字符串等。为了达到节省内存的目的,这些资源在内存中并不是每个应用进程单独一份拷贝。而是会在所有应用之间共享,因为所有应用进程都是作为Zygote进程fork出来的子进程。关于这部分内容,我们已经在Android系统中的进程管理:进程的创建一文中讲解过。

在Java语言中,通过new创建的对象都会在堆中分配内存。应用程序堆的大小是有限的。系统会根据设备的物理内存大小来确定每个应用程序所允许使用的内存大小,一旦应用程序使用的内存超过这个大小,便会发生OutOfMemoryError

因此开发者需要关心应用的内存使用状况。关于如何监测应用程序的内存使用,可以参见这里:Investigating Your RAM Usage

3、开发者相关的API

下面是一些与内存相关的开发者API,它们是Android SDK的一部分。

3.1 ComponentCallbacks2

Android系统会根据当前的系统内存状态和应用的自身状态对应用进行通知。这种通知的目的是希望应用能够感知到系统和自身的状态变化,以便开发者可以更准确的把握应用的运行。

例如:在系统内存充足时,为了提升响应性能,应用可以缓存更多的资源。但是当系统内存紧张时,开发者应当释放一定的资源来缓解内存紧张的状态。

ComponentCallbacks2接口中的void onTrimMemory(int level) 回调函数用来接收这个通知。关于这一点,在“开发者的内存回收”一节,我们会详细讲解。

3.2 ActivityManager

ActivityManager,从名称中就可以看出,这个类是用来管理Activity的系统服务。但这个类中也包含了很多运行时状态查询的接口,这其中就包括与内存相关的几个:

  • int getMemoryClass () 获取当前设备上,单个应用的内存大小限制,单位是M。注意,这个函数的返回值只是一个大致的值。
  • void getMemoryInfo (ActivityManager.MemoryInfo outInfo) 获取系统的内存信息,具体结构可以查看ActivityManager.MemoryInfo,开发者最关心的可能就是availMem以及totalMem。
  • void getMyMemoryState (ActivityManager.RunningAppProcessInfo outState) 获取调用进程的内存信息
  • MemoryInfo[] getProcessMemoryInfo (int[] pids) 通过pid获取指定进程的内存信息
  • boolean isLowRamDevice() 查询当前设备是否是低内存设备

3.3 Runtime

Java应用程序都会有一个Runtime接口的实例,通过这个实例可以查询运行时的一些状态,与内存相关的接口有:

  • freeMemory() 获取Java虚拟机的剩余内存
  • maxMemory() 获取Java虚拟机所能使用的最大内存
  • totalMemory() 获取Java虚拟机拥有的最大内存

4、虚拟机的垃圾回收

垃圾回收是指:虚拟机会监测应用程序的对象创建和使用,并在一些特定的时候销毁无用的对象以回收内存。

垃圾回收的基本想法是要找出虚拟机中哪些对象已经不会再被使用然后将其释放。其最常用的算法有下面两种:

引用计数算法

引用计数算法是为每个对象维护一个被引用的次数:对象刚创建时的初始引用计数为0,每次被一个对象引用时,引用计数加1,反之减1。当一个对象的引用计数重新回到0时便可以认为是不会被使用的,这些对象便可以被垃圾回收。

读者可能马上会想到,当有两个对象互相引用时,这时引用计数该如何计算。关于这部分内容,这里不再展开讲解。有兴趣的读者可以查询Google或者维基百科:Garbage collection

4.1 对象追踪算法

对象追踪算法是通过GC root类型的对象为起点,追踪所有被这些对象所引用的对象,并顺着这些被引用的对象继续往下追踪,在追踪的过程中,对所有被追踪到的对象打上标记。

而剩下的那些没有被打过标记的对象便可以认为是没有被使用的,因此这些对象可以将其释放。

这里提到的的GC root类型的对象有四类:

  • 栈中的local变量,即方法中的局部变量
  • 活动的线程(例如主线程或者开发者创建的线程)
  • static变量
  • JNI中的引用

下面这幅图描述了这种算法:

a)表示算法开始时,所有对象的标记为false,然后以GC root为起点开始追踪和打标记,b)中被追踪到的对象打上了标记。剩下的没有打上标记的对象便可以释放了。算法结束之后,c)中将所有对象的标记全部置为false。下一轮计算时,重新以GC root开始追踪。

Dalvik虚拟机用的就是对象追踪算法,这里是其源码:MarkSweep.cpp

5、开发者的内存回收

内存回收并不是仅仅是系统的事情,作为开发者,也需要在合适的场合下进行内存释放。无节制的消耗内存将导致应用程序OutOfMemoryError

上文中提到,虚拟机的垃圾回收会回收那些不会再被使用到的对象。因此,开发者所需要做的就是:当确定某些对象不会再被使用时,要主动释放对其引用,这样虚拟机才能将其回收。对于不再被用到对象,仍然保持对其引用导致其无法释放,将导致内存泄漏的发生。

为了更好的进行内存回收,系统会一些场景下会通知应用,希望应用能够配合进行一些内存的释放。

ComponentCallbacks2接口中的 void onTrimMemory(int level)回调就是用来接收这个事件的。

Activity, Service, ContentProvider和Application都实现了这个接口,因此这些类的子类都可以接收这个事件。

onTrimMemory回调的参数是一个级别,系统会根据应用本身的状态以及系统的内存状态发送不同的级别,具体的包括:

  • 应用处于Runnig状态可能收到的级别
    • TRIM_MEMORY_RUNNING_MODERATE 表示系统内存已经稍低
    • TRIM_MEMORY_RUNNING_LOW 表示系统内存已经相当低
    • TRIM_MEMORY_RUNNING_CRITICAL 表示系统内存已经非常低,你的应用程序应当考虑释放部分资源
  • 应用的可见性发生变化时收到的级别
    • TRIM_MEMORY_UI_HIDDEN 表示应用已经处于不可见状态,可以考虑释放一些与显示相关的资源
  • 应用处于后台时可能收到的级别
    • TRIM_MEMORY_BACKGROUND 表示系统内存稍低,你的应用被杀的可能性不大。但可以考虑适当释放资源
    • TRIM_MEMORY_MODERATE 表示系统内存已经较低,当内存持续减少,你的应用可能会被杀死
    • TRIM_MEMORY_COMPLETE 表示系统内存已经非常低,你的应用即将被杀死,请释放所有可能释放的资源

这里是这个方法实现的示例代码:Release memory in response to events

在前面的文章中我们提到过:ActivityManagerService负责管理所有的应用进程。

而这里的通知也是来自ActivityManagerService。在updateOomAdjLocked的时候,ActivityManagerService会根据系统内存以及应用的状态通过app.thread.scheduleTrimMemory发送通知给应用程序。

这里的app是ProcessRecord,即描述应用进程的对象,thread是应用的主线程。而scheduleTrimMemory是通过Binder IPC的方式将消息发送到应用进程上。这些内容在前面的文章中已经介绍过,如果觉得陌生,可以阅读一下前面两篇文章。

在ActivityThread中(这个是应用程序的主线程),接受到这个通知之后,便会遍历应用进程中所有能接受这个通知的组件,然后逐个回调通知。

相关代码如下:

final void handleTrimMemory(int level) {
   if (DEBUG_MEMORY_TRIM) Slog.v(TAG, "Trimming memory to level: " + level);

   ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(true, null);

   final int N = callbacks.size();
   for (int i = 0; i < N; i++) {
       callbacks.get(i).onTrimMemory(level);
   }

   WindowManagerGlobal.getInstance().trimMemory(level);
}

6、Linux OOM Killer

前面提到的机制都是在进程内部通过释放对象来进行内存回收。

而实际上,系统中运行的进程数量,以及每个进程所消耗的内存都是不确定的。

在极端的情况下,系统的内存可能处于非常严峻的状态,假设这个时候所有进程都不愿意释放内存,系统将会卡死。

为了使系统能够继续运转不至于卡死,系统会尝试杀死一些不重要的进程来进行内存回收,这其中涉及的模块主要是:Linux OOM Killer和LowMemoryKiller。

Linux OOM Killer是Linux内核的一部分,其源码可以在这里查看:/mm/oom_kill.c

Linux OOM Killer的基本想法是:

当系统已经没法再分配内存的时候,内核会遍历所有的进程,对每个进程计算badness值,得分(badness)最高的进程将会被杀死

即:badness得分越低表示进程越重要,反之表示不重要。

Linux OOM Killer的执行流程如下:

_alloc_pages -> out_of_memory() -> select_bad_process() -> oom_badness()

这其中,_alloc_pages 是内核在分配内存时调用的函数。当内核发现无法再分配内存时,便会计算每个进程的badness值,然后选择最大的(系统认为最不重要的)将其杀死。

那么,内核是如何计算进程的badness值的呢?请看下面的代码:

unsigned long oom_badness(struct task_struct *p, struct mem_cgroup *memcg,
			  const nodemask_t *nodemask, unsigned long totalpages)
{
	long points;
	long adj;

	...

	points = get_mm_rss(p->mm) + p->mm->nr_ptes + get_mm_counter(p->mm, MM_SWAPENTS);
	task_unlock(p);

	if (has_capability_noaudit(p, CAP_SYS_ADMIN))
		points -= (points * 3) / 100;

	adj *= totalpages / 1000;
	points += adj;

	return points > 0 ? points : 1;
}

从这段代码中,我们可以看到,影响进程badness值的因素主要有三个:

  • 进程的oom_score_adj值
  • 进程的内存占用大小
  • 进程是否是root用户的进程

即,oom_score_adj(关于oom_score_adj,在Android系统中的进程管理:进程的优先级一文中我们专门讲解过。)值越小,进程占用的内存越小,并且如果是root用户的进程,系统就认为这个进程越重要。

反之则被认为越不重要,越容易被杀死。

7、LowMemoryKiller

OOM Killer是在系统内存使用情况非常严峻的时候才会起作用。但直到这个时候才开始杀死进程来回收内存是有点晚的。因为在进程被杀死之前,其他进程都无法再申请内存了。

因此,Google在Android上新增了一个LowMemoryKiller模块。LowMemoryKiller通常会在Linux OOM Killer工作之前,就开始杀死进程。

LowMemoryKiller的做法是:

提供6个可以设置的内存级别,当系统内存每低于一个级别时,将oom_score_adj大于某个指定值的进程全部杀死。

这么说会有些抽象,但具体看一下LowMemoryKiller的配置文件我们就好理解了。

LowMemoryKiller在sysfs上暴露了两个文件来供系统调整参数,这两个文件的路径是:

  • /sys/module/lowmemorykiller/parameters/minfree
  • /sys/module/lowmemorykiller/parameters/adj

如果你手上有一个Android设备,你可以通过adb shell连上去之后,通过cat命令查看这两个文件的内容。

这两个文件是配对使用的,每个文件中都是由逗号分隔的6个整数值。

在某个设备上,这两个文件的值可能分别是下面这样:

  • 18432,23040,27648,32256,55296,80640
  • 0,100,200,300,900,906

这组配置的含义是;当系统内存低于80640k时,将oom_score_adj值大于906的进程全部杀死;当系统内存低于55296k时,将oom_score_adj值大于900的进程全部杀死,其他类推。

LowMemoryKiller杀死进程的时候会在内核留下日志,你可以通过dmesg 命令中看到。这个日志可能是这样的:

lowmemorykiller: Killing 'gnunet-service-' (service adj 0,
to free 327224kB on behalf of 'kswapd0' (21) because
cache 6064kB is below limit 6144kB for oom_score_adj 0

从这个日志中,我们可以看到被杀死进程的名称,进程pid和oom_score_adj值。另外还有系统在杀死这个进程之前系统内存还剩多少,以及杀死这个进程释放了多少。

LowMemoryKiller的源码也在内核中,路径是:kernel/drivers/staging/android/lowmemorykiller.c。

lowmemorykiller.c中定义了如下几个函数:

  • lowmem_shrink
  • lowmem_init
  • lowmem_exit
  • lowmem_oom_adj_to_oom_score_adj
  • lowmem_autodetect_oom_adj_values
  • lowmem_adj_array_set
  • lowmem_adj_array_get
  • lowmem_adj_array_free

LowMemoryKiller本身是一个内核驱动程序的形式存在,lowmem_init和lowmem_exit 分别负责模块的初始化和退出清理工作。

在lowmem_init函数中,就是通过register_shrinker向内核中注册了register_shrinker 函数:

static int __init lowmem_init(void)
{
	register_shrinker(&lowmem_shrinker);
	return 0;
}

register_shrinker函数就是LowMemoryKiller的算法核心,这个函数的代码和说明如下:

static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc)
{
	struct task_struct *tsk;
	struct task_struct *selected = NULL;
	int rem = 0;
	int tasksize;
	int i;
	short min_score_adj = OOM_SCORE_ADJ_MAX + 1;
	int minfree = 0;
	int selected_tasksize = 0;
	short selected_oom_score_adj;
	int array_size = ARRAY_SIZE(lowmem_adj);
	int other_free = global_page_state(NR_FREE_PAGES) - totalreserve_pages;
	int other_file = global_page_state(NR_FILE_PAGES) -
						global_page_state(NR_SHMEM) -
						total_swapcache_pages();

	if (lowmem_adj_size < array_size)
		array_size = lowmem_adj_size;
	if (lowmem_minfree_size < array_size)
		array_size = lowmem_minfree_size;
	// lowmem_minfree 和lowmem_adj 记录了两个配置文件中配置的数据
	for (i = 0; i < array_size; i++) {
		minfree = lowmem_minfree[i];
		// 确定当前系统处于低内存的第几档
		if (other_free < minfree && other_file < minfree) {
		   // 确定需要杀死的进程的oom_score_adj的上限
			min_score_adj = lowmem_adj[i];
			break;
		}
	}
	if (sc->nr_to_scan > 0)
		lowmem_print(3, "lowmem_shrink %lu, %x, ofree %d %d, ma %hd\n",
				sc->nr_to_scan, sc->gfp_mask, other_free,
				other_file, min_score_adj);
	rem = global_page_state(NR_ACTIVE_ANON) +
		global_page_state(NR_ACTIVE_FILE) +
		global_page_state(NR_INACTIVE_ANON) +
		global_page_state(NR_INACTIVE_FILE);
	if (sc->nr_to_scan <= 0 || min_score_adj == OOM_SCORE_ADJ_MAX + 1) {
		lowmem_print(5, "lowmem_shrink %lu, %x, return %d\n",
			     sc->nr_to_scan, sc->gfp_mask, rem);
		return rem;
	}
	selected_oom_score_adj = min_score_adj;

	rcu_read_lock();
	// 遍历所有进程
	for_each_process(tsk) {
		struct task_struct *p;
		short oom_score_adj;

		if (tsk->flags & PF_KTHREAD)
			continue;

		p = find_lock_task_mm(tsk);
		if (!p)
			continue;

		if (test_tsk_thread_flag(p, TIF_MEMDIE) &&
		    time_before_eq(jiffies, lowmem_deathpending_timeout)) {
			task_unlock(p);
			rcu_read_unlock();
			return 0;
		}
		oom_score_adj = p->signal->oom_score_adj;
		// 跳过那些oom_score_adj值比目标值小的
		if (oom_score_adj < min_score_adj) {
			task_unlock(p);
			continue;
		}
		tasksize = get_mm_rss(p->mm);
		task_unlock(p);
		if (tasksize <= 0)
			continue;
		// selected 是将要杀死的备选进程
		if (selected) {
		   // 跳过那些oom_score_adj比备选的小的
			if (oom_score_adj < selected_oom_score_adj)
				continue;
		   // 如果oom_score_adj一样,跳过那些内存消耗更小的
			if (oom_score_adj == selected_oom_score_adj &&
			    tasksize <= selected_tasksize)
				continue;
		}
		// 更换备选的目标,因为又发现了一个oom_score_adj更大,
		// 或者内存消耗更大的进程
		selected = p;
		selected_tasksize = tasksize;
		selected_oom_score_adj = oom_score_adj;
		lowmem_print(2, "select '%s' (%d), adj %hd, size %d, to kill\n",
			     p->comm, p->pid, oom_score_adj, tasksize);
	}

	// 已经选中目标,记录日志并杀死进程
	if (selected) {
		long cache_size = other_file * (long)(PAGE_SIZE / 1024);
		long cache_limit = minfree * (long)(PAGE_SIZE / 1024);
		long free = other_free * (long)(PAGE_SIZE / 1024);
		trace_lowmemory_kill(selected, cache_size, cache_limit, free);
		lowmem_print(1, "Killing '%s' (%d), adj %hd,\n" \
				"   to free %ldkB on behalf of '%s' (%d) because\n" \
				"   cache %ldkB is below limit %ldkB for oom_score_adj %hd\n" \
				"   Free memory is %ldkB above reserved\n",
			     selected->comm, selected->pid,
			     selected_oom_score_adj,
			     selected_tasksize * (long)(PAGE_SIZE / 1024),
			     current->comm, current->pid,
			     cache_size, cache_limit,
			     min_score_adj,
			     free);

		lowmem_deathpending_timeout = jiffies + HZ;
		set_tsk_thread_flag(selected, TIF_MEMDIE);
		send_sig(SIGKILL, selected, 0);
		rem -= selected_tasksize;
	}
	lowmem_print(4, "lowmem_shrink %lu, %x, return %d\n",
		     sc->nr_to_scan, sc->gfp_mask, rem);
	rcu_read_unlock();
	return rem;
}

8、进程的死亡处理

在任何时候,应用进程都可能死亡,例如被OOM Killer或者LowMemoryKiller杀死,自身crash死亡又或者被用户手动杀死。无论哪种情况,作为应用进程的管理者ActivityManagerService都需要知道。

在应用进程死亡之后,ActivityManagerService需要执行如下工作:

  • 执行清理工作 ActivityManagerService内部的ProcessRecord以及可能存在的四大组件的相关结构需要全部清理干净
  • 重新计算进程的优先级 上文已经提到过,进程的优先级是有关联性的,有其中一个进程死亡了,可能会连到影响到其他进程的优先级需要调整。

ActivityManagerService是利用Binder提供的死亡通知机制来进行进程的死亡处理的。关于Binder请参阅其他资料,限于篇幅关系,这里不再展开讲解。

简单来说,死亡通知机制就提供了进程间的一种死亡监听的能力:当目标进程死亡的时候,监听回调会执行。

ActivityManagerService中的AppDeathRecipient监听了应用进程的死亡消息,该类代码如下:

private final class AppDeathRecipient implements IBinder.DeathRecipient {
   final ProcessRecord mApp;
   final int mPid;
   final IApplicationThread mAppThread;

   AppDeathRecipient(ProcessRecord app, int pid,
           IApplicationThread thread) {
       mApp = app;
       mPid = pid;
       mAppThread = thread;
   }

   @Override
   public void binderDied() {
       synchronized(ActivityManagerService.this) {
           appDiedLocked(mApp, mPid, mAppThread, true);
       }
   }
}

每一个应用进程在启动之后,都会attach到ActivityManagerService上通知它自己的进程已经启动完成了。这时ActivityManagerService便会为其创建一个死亡通知的监听器。在这之后如果进程死亡了,ActivityManagerService便会收到通知。

private final boolean attachApplicationLocked(IApplicationThread thread,
       int pid) {
    ...
        try {
            AppDeathRecipient adr = new AppDeathRecipient(
                    app, pid, thread);
            thread.asBinder().linkToDeath(adr, 0);
            app.deathRecipient = adr;
        } catch (RemoteException e) {
            app.resetPackageList(mProcessStats);
            startProcessLocked(app, "link fail", processName);
            return false;
        }
    ...
}

进程死亡之后的处理工作是appDiedLocked这个方法中处理的,这部分还是比较容易理解的,这里就不过多讲解了。

原文地址:

Android系统中的进程管理:内存的回收

Android系统中的进程管理:进程的创建

Android系统中的进程管理:进程的优先级

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 视觉几何三维重建是指利用摄像机拍摄的多个视角下的图像,通过计算机图形学算法获得三维模型的过程。其中,openmvs是一种基于MVS(多视角几何重建)实现的三维重建工具。 OpenMVS是一个开源的三维重建工具,基于MVS算法,可以实现从多个图像中生成高精度的三维模型。OpenMVS的几何重建算法主要是采用光束法,通过对图像进行矩阵重建来计算相机位置和三角形点云。OpenMVS的几何重建方法相对于其他算法具有较高的稳定性和精度。 在OpenMVS的源码分析中,主要包括三个部分:几何重建、点云和网格处理。几何重建是基于多视角几何的,通过将多个图像的视角转化到同一个坐标系中,可以计算出三角形的点云。点云处理主要包括点云优化和稠密重建。网格处理则是在点云的基础上生成三角形网格模型。 OpenMVS的优势在于能够充分利用多视图几何的信息,提高三维重建的精度和效率。而且该工具具有良好的可扩展性和适应性,可以在不同场景下应用。同时,OpenMVS的开源代码也为研究者提供了一个可靠的研究平台,进行更深入的算法研究和开发。 总之,视觉几何三维重建是一项非常复杂的任务,而OpenMVS作为一个优秀的三维重建工具,通过独特的几何重建算法和优秀的可扩展性,加速了三维重建的研究和应用。 ### 回答2: 首先,视觉几何三维重建是一项重要的计算机视觉技术,其主要目的是利用多视角图像或视频序列来恢复场景的三维结构。在该过程中,重建算法必须解决诸多技术难题,如图像匹配、相机姿态估计、点云配准、三维重建等。 而OpenMVS则是一款优秀的三维重建软件,其核心算法基于多视图几何,能够高效、精确地处理大规模三维数据。具体来说,OpenMVS采用稀疏点云表示法(Sparse Point Cloud)和密集点云表示法(Dense Point Cloud)来表示场景中的点云信息,其中稀疏点云用于初始匹配,密集点云用于表面重建。 在实现中,OpenMVS采用先进的图像流水线(Image Pipeline)来处理输入的图像序列,包括预处理(Pre-processing)、特征提取(Feature Extraction)、特征匹配(Feature Matching)等多个步骤。在此基础上,OpenMVS还提供了多种优化方法,如基于非线性优化的相机姿态估计、自适应曲率滤波等,以进一步提高重建效果。 值得指出的是,OpenMVS作为一款开源软件,其源代码也是完全开放的。此外,OpenMVS还具有友好的用户界面和丰富的文档,能够帮助用户快速上手并实现高质量的三维重建。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值