当前位置: 首页 > news >正文

丹阳网站建设咨询网站打开后显示建设中

丹阳网站建设咨询,网站打开后显示建设中,成都必去景点排名,请列出五个以上做外贸的网站一、进程的创建 1、概述 Android系统以Linux内核为基础#xff0c;所以对于进程的管理自然离不开Linux本身提供的机制。例如#xff1a; 通过fork来创建进行通过信号量来管理进程通过proc文件系统来查询和调整进程状态 等 对于Android来说#xff0c;进程管理的主要内容…一、进程的创建 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全部四大组件ActivityServiceContentProviderBroadcastReceiver的管理并且还掌控了所有应用程序进程的创建和进程的优先级管理。 因此这个部分的内容将是本系列文章讲解的重点。 3、进程与线程 Android官方开发网站的这篇文章Processes and Threads 非常好的介绍了Android系统中进程相关的一些基本概念和重要知识。 在阅读下文之前请务必将这篇文章浏览一遍。 4、关于进程 在Android系统中进程可以大致分为系统进程和应用进程两大类。 系统进程是系统内置的例如initzygotesystem_server进程属于操作系统必不可少的一部分。系统进程的作用在于 管理硬件设备提供访问设备的基本能力管理应用进程 应用进程是指应用程序运行的进程。这些应用程序可能是系统出厂自带的例如Launcher电话短信等应用也可能是用户自己安装的例如微信支付宝等。 系统进程的数量通常是固定的出厂或者系统升级之后就确定了并且系统进程通常是一直存活常驻内存的。系统进程的异常退出将可能导致设备无法正常使用。 而应用程序和应用进程在每个人使用的设备上通常是各不一样的。如何管理好这些不确定的应用进程就是操作系统本身要仔细考虑的内容。也是衡量一个操作系统好坏的标准之一。 在本文中我们会介绍initzygote和system_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中配置了系统启动的时候该做哪些事情以及启动哪些系统进程。 这其中有两个特别重要的进程就是zygote和system_server进程。 zygote的中文意思是“受精卵“。这是一个很有寓意的名称所有的应用进程都是由zygote fork出来的子进程因此zygote进程是所有应用进程的父进程。system_server 这个进程正如其名称一样这是一个系统服务器。Framework层的几乎所有服务都位于这个进程中。这其中就包括管理四大组件的ActivityManagerService。 6、Zygote进程 init.rc文件会根据平台不一样选择下面几个文件中的一个来启动zygote进程 init.zygote32.rcinit.zygote32_64.rcinit.zygote64.rcinit.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;} } 在这段代码中我们主要关注如下几行 通过 registerZygoteSocket(socketName); 注册Zygote Socket通过 preload(); 预先加载所有应用都需要的公共资源通过 startSystemServer(abiList, socketName); 启动system_server通过 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 ProcessMapProcessRecord mProcessNames new ProcessMapProcessRecord(); 按pid组织的集合 /** * All of the processes we currently have running organized by pid. * The keys are the pid running the application. * * pNOTE: This object is protected by its own lock, NOT the global * activity manager lock! */ final SparseArrayProcessRecord mPidsSelfLocked new SparseArrayProcessRecord(); 下面这幅图小节了上文的这些内容 9、关于应用组件 Processes and Threads 提到 “当某个应用组件启动且该应用没有运行其他任何组件时Android 系统会使用单个执行线程为应用启动新的 Linux 进程。” 因此四大组件中的任何一个先起来都会导致应用进程的创建。下文我们就详细看一下它们启动时各自是如何导致应用进程的创建的。 PS四大组件的管理本身又是一个比较大的话题限于篇幅关系这里不会非常深入的讲解这里主要是讲解四大组件与进程创建的关系。 在应用程序中开发者通过 startActivity(Intent intent) 来启动ActivitystartService(Intent service) 来启动ServicesendBroadcast(Intent intent) 来发送广播ContentResolver 中的接口来使用ContentProvider 这其中startActivitystartService和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包含多个TaskActivityStackSupervisor类负责管理所有的StackActivity的启动过程会牵涉到 Intent的解析StackTask的查询或创建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 activitys 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 ((appmAm.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.curAppmService.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 如果你还没有阅读请立即阅读一下这篇文章一文中我们已经了解到系统对于进程的优先级有如下五个分类 前台进程可见进程服务进程后台进程空进程 实际上这只是一个粗略的划分。在系统的内部实现中优先级远不止这么五种。 2、优先级的依据 我们来简单列一下应用组件与进程的相关信息 每一个Android的应用进程中都可能包含四大组件中的一个/种或者多个/种。对于运行中的Service和ContentProvider来说可能有若干个客户端进程正在对其使用。应用进程是由ActivityManagerService发送请求让zygote创建的并且ActivityManagerService中对于每一个运行中的进程都有一个ProcessRecord对象与之对应。 ProcessRecord简化图如下所示 在ProcessRecord中详细记录了应用组件的相关信息相关代码如下 // all activities running in the process final ArrayListActivityRecord activities new ArrayList(); // all ServiceRecord running in this process final ArraySetServiceRecord services new ArraySet(); // services that are currently executing code (need to remain foreground). final ArraySetServiceRecord executingServices new ArraySet(); // All ConnectionRecord this process holds final ArraySetConnectionRecord connections new ArraySet(); // all IIntentReceivers that are registered from this process. final ArraySetReceiverList receivers new ArraySet(); // class (String) - ContentProviderRecord final ArrayMapString, ContentProviderRecord pubProviders new ArrayMap(); // All ContentProviderRecord process is using final ArrayListContentProviderConnection conProviders new ArrayList(); 这里的 activities 记录了进程中运行的ActivityservicesexecutingServices 记录了进程中运行的Servicereceivers 记录了进程中运行的BroadcastReceiverpubProviders 记录了进程中运行的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_ADJPERCEPTIBLE_APP_ADJPREVIOUS_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 -700PERSISTENT_PROC_ADJ -800。 另外还有一些系统服务的实现如果这些系统服务不存在系统将无法工作所以这些应用的优先级最高几乎是任何任何时候都需要存在的SYSTEM_ADJ -900NATIVE_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 managers version of Process.THREAD_GROUP_BG_NONINTERACTIVE static final int SCHED_GROUP_BACKGROUND 0; // Activity managers version of Process.THREAD_GROUP_DEFAULT static final int SCHED_GROUP_DEFAULT 1; // Activity managers version of Process.THREAD_GROUP_TOP_APP static final int SCHED_GROUP_TOP_APP 2; // Activity managers 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.确认该进程是否是空进程空进程中没有任何组件因此主线程也为nullProcessRecord.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.curAdjapp.curRawAdjProcessList.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.curAdjapp.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优先级也是被认为是较高优先级的 该Activity处于可见状态该Activity处于Pause正在Pause状态该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 saner.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--) {ArrayListConnectionRecord 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.收尾工作 收尾工作主要是根据进程中的ServiceProvider的一些特殊状态做一些处理另外还有针对空进程以及设置了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 方法负责了此项工作。 前文中我们提到优先级意味着三个方面这里的生效就对应了这三个方面 ProcessList.setOomAdj(app.pid, app.info.uid, app.curAdj); 将计算出来的adj值写入到procfs中即/proc/[pid]/oom_score_adj 这个文件中。Process.setProcessGroup(app.pid, processGroup); 用来设置进程的调度组。app.thread.setProcessState(app.repProcState); 这个方法会最终调用到 VMRuntime.getRuntime().updateProcessState();将进程的状态设置到虚拟机中。 7、结束语 前言中我们提到“优先级反应了系统对于进程重要性的判定。” 那么系统如何评价进程的优先级便是系统本身一个很重要的特性。了解系统的这一特性对于我们开发应用程序以及对于应用程序运行的行为分析是很有意义的。 系统在判定优先级的时候应当做到公平公正并且不能让开发者有机可乘。 “公平公正”是指系统需要站在一个中间人的状态下不偏倚任何一个应用公正的将系统资源分配给真正需要的进程。并且在系统资源紧张的时候回收不重要的进程。 通过上文的分析我们看到Android系统认为“重要”的进程主要有三类 系统进程前台与用户交互的进程前台进程所使用到的进程 不过对于这一点是有改进的空间的例如可以引入用户习惯的分析如果是用户频繁使用的应用可以给予这些应用更高的优先级以提升这些应用的响应速度。目前国内一些Android定制厂商已经开始做这类功能的支持。 “不能让开发者有机可乘”是指系统对于进程优先级的判定的因素应当是不能被开发者利用的。因为一旦开发者可以利用每个开发者都肯定会将自己的设置为高优先级来抢占更多的资源。 需要说明的是Android在这个方面是存在缺陷的在Android系统上可以通过startForeground拿到前台的优先级的。后来Google也意识到这个问题于是在API Level 18以上的版本上调用startForeground这个API会在通知栏显示一条通知以告知用户。但是这个改进是有Bug的开发者可以同时通过startForeground启动两个Service指定同样的通知id然后退出其中一个这样应用的不会在通知栏显示通知图标并且拿到了前台的优先级。这个便是让开发者“有机可乘”了。 三、内存的回收 1、前言 内存是系统中非常宝贵的资源即便如今的移动设备上内存已经达到4G甚至6G的级别但对于内存的回收也依然重要因为在Android系统上同时运行的进程有可能会有几十甚至上百个之多。 如何将系统内存合理的分配给每个进程以及如何进行内存回收便是操作系统需要处理的问题之一。 本文会讲解Android系统中内存回收相关的知识。 对于内存回收主要可以分为两个层次 进程内的内存回收通过释放进程中的资源进行内存回收进程级的内存回收通过杀死进程来进行内存回收 这其中进程内的内存回收主要分为两个方面 虚拟机自身的垃圾回收机制在系统内存状态发生变化时通知应用程序让开发者进行内存回收 而进程级的内存回收主要是依靠系统中的两个模块它们是 Linux OOM KillerLowMemoryKiller 在特定场景下他们都会通过杀死进程来进行内存回收。 下图描述了几种内存回收机制 2、Android系统的内存管理简介 在Android系统中进程可以大致分为系统进程和应用进程两大类。 系统进程是系统内置的例如initzygotesystem_server进程属于操作系统必不可少的一部分。系统进程的作用在于 管理硬件设备提供访问设备的基本能力管理应用进程 应用进程是指应用程序运行的进程。这些应用程序可能是系统出厂自带的例如Launcher电话短信等应用也可能是用户自己安装的例如微信支付宝等。 Android中应用进程通常都运行在Java虚拟机中。在Android 5.0之前的版本这个虚拟机是Dalvik5.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);ArrayListComponentCallbacks2 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,806400,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_shrinklowmem_initlowmem_exitlowmem_oom_adj_to_oom_score_adjlowmem_autodetect_oom_adj_valueslowmem_adj_array_setlowmem_adj_array_getlowmem_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;}Overridepublic 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系统中的进程管理进程的优先级
http://www.hkea.cn/news/14489157/

相关文章:

  • 上海网站建设平台教你做吃的网站
  • 联想服务器怎么建设第二个网站马尾网站建设
  • 公司名称被大量网站收录电商网站开发难点
  • 做外贸站推广做爰片的网站
  • 室内设计网站界面wordpress 手机端APP
  • 做网站都能赚钱吗有的网站在浏览器打不开怎么办
  • 网站建设费用写创意自己做网站推广试玩
  • 电子商务网站建设实验指导网站建设总结 优帮云
  • 设计相关网站西安模板网站建设
  • 海口网站建设做网站中国建设网平台
  • 腾龙官方网站做号软件设计网站的步骤
  • 做效果图去哪个网站接活电商设计招聘
  • 农产品网站建设策划html总结心得体会小短篇
  • 一个域名一个ip做多个网站广州建设工程中心网站
  • 网站制作厦门公司网站 模板 下载
  • 网上网站开发村级网站建站
  • 佛山做外贸网站案例百度搜索收录
  • 企业网站必须备案吗以下属于网站seo的内容是
  • 无需下载国外黄冈网站推广directadmin备份网站
  • 广州建设高端网站怎么用ps做网站首页图片尺寸
  • 室内设计网站公司建邺做网站价格
  • 个人站长怎么做企业网站什么是h5网站
  • 网站外链建设可以提升网站权重吗wordpress怎么改为中文
  • 公司网站建设沈阳广州企业网站制作公司
  • 一站式网站搭建企业网站推广哪家好
  • 公司模板网站建设福州网站搜索引擎优化
  • 厦门网站设计推广公司国外 网站设计
  • 建设广告网站费用保险公司发展规划
  • 宝安龙华积分商城网站建设佛山网站建设专家
  • 二手书店网站建设规划书wordpress 多站点 子目录