Android 中app内存回收优化(二):S 版本

版本基于:Android S

0. 前言

Android Q 中新增了framework 端app 内存回收优化方案。当app 的 oom adj 发生特定变化时,framework 端会对应用的内存进行处理。随着版本的演变,这部分优化工作也一直在完善,笔者将针对 Android RAndroid S 对该部分的优化流程分别进行详细地剖析。

上一文中,针对Android R 版本进行了详细的剖析,本文继续剖析 Android S 版本。

注意:

本文中提到的 “压缩” 这个词,其实指的是内存回收优化,因为只有到确切的逻辑的时候才明确到底是匿名页回收还是文件页回收,而在此之前我们暂定为 compact 处理。

另外,本文分析是对比于 Android R 版本,最后也会总结下两个版本之间的差异和优缺点。

1. CachedAppOptimizer 类

App 端的内存压缩管理是在 CachedAppOptimizer 类中完成。

我们在之前的博文《oom_adj 更新原理(1)》《oom_adj 更新原理(2)》中得知 AMS 通过 OomAdjuster 类来管理 oom_adj 的更新、计算、应用。在 OomAdjuster 中也实例了一个 CachedAppOptimizer 的对象:

frameworks/base/services/core/java/com/android/server/am/OomAdjuster.javaOomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids,ServiceThread adjusterThread) {mService = service;...mCachedAppOptimizer = new CachedAppOptimizer(mService);...}

参数为 AMS 对象。

下面来看下 CachedAppOptimizer 构造

frameworks/base/services/core/java/com/android/server/am/CachedAppOptimizer.javapublic CachedAppOptimizer(ActivityManagerService am) {this(am, null, new DefaultProcessDependencies());}@VisibleForTestingCachedAppOptimizer(ActivityManagerService am, PropertyChangedCallbackForTest callback,ProcessDependencies processDependencies) {mAm = am;mProcLock = am.mProcLock;mCachedAppOptimizerThread = new ServiceThread("CachedAppOptimizerThread",mCompactionPriority, true);mProcStateThrottle = new HashSet<>();mProcessDependencies = processDependencies;mTestCallback = callback;mSettingsObserver = new SettingsContentObserver();mProcLocksReader = new ProcLocksReader();}

构造中创建了一个 ServiceThread,名称为 CachedAppOptimizerThread,优先级为THREAD_GROUP_SYSTEM

mProcessDependencies 是 DefaultProcessDependencies类型的对象,用以最后的压缩处理。

与 Android R 差异的地方:

  • 新加了 mProcLock 变量,用以同步 AMS 中的进程管理;
  • 新加了 mSettingsObserver 变量,用以监听 cached_apps_freezer 发生变化;
  • 新加了 mProcLocksReader 变量,用以冻结进程时查看 /proc/locks 信息;

2. init()

不同于 Android R 版本,Android R 中 的 init() 函数是在 SystemServer.startOtherServices() 的时候调用 AMS 中installSystemProviders() 函数触发,在 installSystemProviders() 会调用 mOomAdjuster.initSettings()。

而Android S 中 installSystemProviders() 被定义在 ContentProviderHelper.java 中。

在 OomAdjuster.initSettings() 中会调用到 CachedAppOptimizer.init()。

frameworks/base/services/core/java/com/android/server/am/CachedAppOptimizer.javapublic void init() {DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,ActivityThread.currentApplication().getMainExecutor(), mOnFlagsChangedListener);DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,ActivityThread.currentApplication().getMainExecutor(),mOnNativeBootFlagsChangedListener);mAm.mContext.getContentResolver().registerContentObserver(CACHED_APP_FREEZER_ENABLED_URI, false, mSettingsObserver);synchronized (mPhenotypeFlagLock) {updateUseCompaction();updateCompactionActions();updateCompactionThrottles();updateCompactStatsdSampleRate();updateFreezerStatsdSampleRate();updateFullRssThrottle();updateFullDeltaRssThrottle();updateProcStateThrottle();updateUseFreezer();updateMinOomAdjThrottle();updateMaxOomAdjThrottle();}}

相比于Android R 这里多了个针对冻结属性的监听 mOnNativeBootFlagsChangedListener;

另外,在最后多了两个 update 函数。

2.1 updateUseCompaction()

    private void updateUseCompaction() {mUseCompaction = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,KEY_USE_COMPACTION, DEFAULT_USE_COMPACTION);mCompactionPriority = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,KEY_COMPACTION_PRIORITY, Process.THREAD_GROUP_BACKGROUND);if (mUseCompaction && mCompactionHandler == null) {if (!mCachedAppOptimizerThread.isAlive()) {mCachedAppOptimizerThread.start();}mCompactionHandler = new MemCompactionHandler();}Process.setThreadGroupAndCpuset(mCachedAppOptimizerThread.getThreadId(),mCompactionPriority);}

相比较 Android R,这里将 thread 的优先级进行了动态配置,优先级可以通过 DeviceConfig 的 compaction_priority 属性设置。另外,优先级的设置也不依赖 mUsecompaction 是否使能。

其他与R 都相同,首先获取下属性 use_compaction,默认值使用 DEFAULT_USE_COMPACTION (false):

    @VisibleForTesting static final Boolean DEFAULT_USE_COMPACTION = false;

如果 user_compation 使能,接着就会创建 mCompactionHandler 用以异步消息处理,并且启动在构造函数中创建的ServiceThread。

来看下 MemCompactionHandler 类:

    private final class MemCompactionHandler extends Handler {private MemCompactionHandler() {super(mCachedAppOptimizerThread.getLooper());}@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case COMPACT_PROCESS_MSG: {...break;}case COMPACT_SYSTEM_MSG: {...break;}}}

Looper 是用的就是 ServiceThread 的Looper,主要用以处理压缩进程内存或system 进程内存。

2.2 updateCompactionActions()

    private void updateCompactionActions() {int compactAction1 = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,KEY_COMPACT_ACTION_1, DEFAULT_COMPACT_ACTION_1);int compactAction2 = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,KEY_COMPACT_ACTION_2, DEFAULT_COMPACT_ACTION_2);mCompactActionSome = compactActionIntToString(compactAction1);mCompactActionFull = compactActionIntToString(compactAction2);}

与 Android R 相同

2.3 updateCompactionThrottles()

    private void updateCompactionThrottles() {boolean useThrottleDefaults = false;// TODO: improve efficiency by calling DeviceConfig only once for all flags.String throttleSomeSomeFlag =DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,KEY_COMPACT_THROTTLE_1);String throttleSomeFullFlag =DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,KEY_COMPACT_THROTTLE_2);String throttleFullSomeFlag =DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,KEY_COMPACT_THROTTLE_3);String throttleFullFullFlag =DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,KEY_COMPACT_THROTTLE_4);String throttleBFGSFlag =DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,KEY_COMPACT_THROTTLE_5);String throttlePersistentFlag =DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,KEY_COMPACT_THROTTLE_6);String throttleMinOomAdjFlag =DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,KEY_COMPACT_THROTTLE_MIN_OOM_ADJ);String throttleMaxOomAdjFlag =DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,KEY_COMPACT_THROTTLE_MAX_OOM_ADJ);if (TextUtils.isEmpty(throttleSomeSomeFlag) || TextUtils.isEmpty(throttleSomeFullFlag)|| TextUtils.isEmpty(throttleFullSomeFlag)|| TextUtils.isEmpty(throttleFullFullFlag)|| TextUtils.isEmpty(throttleBFGSFlag)|| TextUtils.isEmpty(throttlePersistentFlag)|| TextUtils.isEmpty(throttleMinOomAdjFlag)|| TextUtils.isEmpty(throttleMaxOomAdjFlag)) {// Set defaults for all if any are not set.useThrottleDefaults = true;} else {try {mCompactThrottleSomeSome = Integer.parseInt(throttleSomeSomeFlag);mCompactThrottleSomeFull = Integer.parseInt(throttleSomeFullFlag);mCompactThrottleFullSome = Integer.parseInt(throttleFullSomeFlag);mCompactThrottleFullFull = Integer.parseInt(throttleFullFullFlag);mCompactThrottleBFGS = Integer.parseInt(throttleBFGSFlag);mCompactThrottlePersistent = Integer.parseInt(throttlePersistentFlag);mCompactThrottleMinOomAdj = Long.parseLong(throttleMinOomAdjFlag);mCompactThrottleMaxOomAdj = Long.parseLong(throttleMaxOomAdjFlag);} catch (NumberFormatException e) {useThrottleDefaults = true;}}if (useThrottleDefaults) {mCompactThrottleSomeSome = DEFAULT_COMPACT_THROTTLE_1;mCompactThrottleSomeFull = DEFAULT_COMPACT_THROTTLE_2;mCompactThrottleFullSome = DEFAULT_COMPACT_THROTTLE_3;mCompactThrottleFullFull = DEFAULT_COMPACT_THROTTLE_4;mCompactThrottleBFGS = DEFAULT_COMPACT_THROTTLE_5;mCompactThrottlePersistent = DEFAULT_COMPACT_THROTTLE_6;mCompactThrottleMinOomAdj = DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ;mCompactThrottleMaxOomAdj = DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ;}}

相比较 Android R,主要这里多了两个针对 adj 的限制值:mCompactThrottleMinOomAdj 和 mCompactThrottleMaxOomAdj。当以 FULL 进行 compact 时,需要上一次的 oom_adj 不是 cached。在 R 版本中 这些条件判断放在 OomAdjuster.applyOomAdjLocked() 函数中,而在 S 版本中将其放到了这里。笔者猜想,这样设计的初衷,本应该是为了让用户对 FULL 压缩的 adj 达到可控,但相比较 applyOomAdjLSP() 函数,还是Android R 更好一些。

2.4 updateFullRssThrottle()

    private void updateFullRssThrottle() {mFullAnonRssThrottleKb = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,KEY_COMPACT_FULL_RSS_THROTTLE_KB, DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);// Don't allow negative values. 0 means don't apply the throttle.if (mFullAnonRssThrottleKb < 0) {mFullAnonRssThrottleKb = DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB;}}

同 Android R 版本

2.5 updateFullDeltaRssThrottle()

    private void updateFullDeltaRssThrottle() {mFullDeltaRssThrottleKb = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB, DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB);if (mFullDeltaRssThrottleKb < 0) {mFullDeltaRssThrottleKb = DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB;}}

同 Android R 版本

2.6 updateProcStateThrottle()

    private void updateProcStateThrottle() {String procStateThrottleString = DeviceConfig.getString(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPACT_PROC_STATE_THROTTLE,DEFAULT_COMPACT_PROC_STATE_THROTTLE);if (!parseProcStateThrottle(procStateThrottleString)) {Slog.w(TAG_AM, "Unable to parse app compact proc state throttle \""+ procStateThrottleString + "\" falling back to default.");if (!parseProcStateThrottle(DEFAULT_COMPACT_PROC_STATE_THROTTLE)) {Slog.wtf(TAG_AM,"Unable to parse default app compact proc state throttle "+ DEFAULT_COMPACT_PROC_STATE_THROTTLE);}}}

同 Android R 版本

2.7 updateUseFreezer()

进程冻结状态初始化,后续将单独剖析下冻结优化。

2.8 updateMinOomAdjThrottle()

    private void updateMinOomAdjThrottle() {mCompactThrottleMinOomAdj = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,KEY_COMPACT_THROTTLE_MIN_OOM_ADJ, DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ);// Should only compact cached processes.if (mCompactThrottleMinOomAdj < ProcessList.CACHED_APP_MIN_ADJ) {mCompactThrottleMinOomAdj = DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ;}}private void updateMaxOomAdjThrottle() {mCompactThrottleMaxOomAdj = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,KEY_COMPACT_THROTTLE_MAX_OOM_ADJ, DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ);// Should only compact cached processes.if (mCompactThrottleMaxOomAdj > ProcessList.CACHED_APP_MAX_ADJ) {mCompactThrottleMaxOomAdj = DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ;}}

这两个函数是 Android S 多出来的,主要是针对 FULL 压缩时判断。

3. 压缩优化发起者

压缩优化的触发分多种情形,在 CachedAppOptimizer 中,压缩优化发起者主要有:

  • compactAppSome()
  • compactAppFull()
  • compactAppPersistent()
  • compactAppBfgs()
  • compactAllSystem()

重要逻辑,与Android R 版本类似,本文主要剖析下前两个函数。

3.1 compactAppSome()

在 OomAdjuster.applyOomAdjLSP() 触发:

    private boolean applyOomAdjLSP(ProcessRecord app, boolean doingAll, long now,long nowElapsed) {...if (mCachedAppOptimizer.useCompaction() && mService.mBooted) {// Cached and prev/home compactionif (state.getCurAdj() != state.getSetAdj()) {// Perform a minor compaction when a perceptible app becomes the prev/home app// Perform a major compaction when any app enters cached// reminder: here, setAdj is previous state, curAdj is upcoming stateif (state.getSetAdj() <= ProcessList.PERCEPTIBLE_APP_ADJ&& (state.getCurAdj() == ProcessList.PREVIOUS_APP_ADJ|| state.getCurAdj() == ProcessList.HOME_APP_ADJ)) {mCachedAppOptimizer.compactAppSome(app);} else if (state.getCurAdj() >= ProcessList.CACHED_APP_MIN_ADJ&& state.getCurAdj() <= ProcessList.CACHED_APP_MAX_ADJ) {mCachedAppOptimizer.compactAppFull(app);}} else if (mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE&& state.getSetAdj() < ProcessList.FOREGROUND_APP_ADJ// Because these can fire independent of oom_adj/procstate changes, we need// to throttle the actual dispatch of these requests in addition to the// processing of the requests. As a result, there is throttling both here// and in CachedAppOptimizer.&& mCachedAppOptimizer.shouldCompactPersistent(app, now)) {mCachedAppOptimizer.compactAppPersistent(app);} else if (mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE&& state.getCurProcState()== ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE&& mCachedAppOptimizer.shouldCompactBFGS(app, now)) {mCachedAppOptimizer.compactAppBfgs(app);}}...}

相比较 Andoird R 版本,这里对 FULL 压缩进行了逻辑调整,把R 中 从 非 cachedcached 的判断,改成了这里只关心目前处于 cached,然后在 compactAppFull() 中再进行确认。笔者认为这样索性将条件完全交给 compactAppFull() 好了啊,不清楚设计的目的。

逻辑剖析这里就省略了,详细可以查看 Android R 版本

    void compactAppSome(ProcessRecord app) {app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_SOME);if (!app.mOptRecord.hasPendingCompact()) {app.mOptRecord.setHasPendingCompact(true);mPendingCompactionProcesses.add(app);mCompactionHandler.sendMessage(mCompactionHandler.obtainMessage(COMPACT_PROCESS_MSG, app.mState.getSetAdj(), app.mState.getSetProcState()));}}

相比较 Android R,ProcessRecord 类中多加了一个 mOptRecord 的成员变量,类型为

ProcessCachedOptimizerRecord,就是为了配合 CachedAppOptimizer 类。

其他逻辑上跟Android R 相同。

3.2 compactAppFull()

    void compactAppFull(ProcessRecord app) {// Apply OOM adj score throttle for Full App Compaction.if ((app.mState.getSetAdj() < mCompactThrottleMinOomAdj|| app.mState.getSetAdj() > mCompactThrottleMaxOomAdj)&& app.mState.getCurAdj() >= mCompactThrottleMinOomAdj&& app.mState.getCurAdj() <= mCompactThrottleMaxOomAdj) {app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_FULL);if (!app.mOptRecord.hasPendingCompact()) {app.mOptRecord.setHasPendingCompact(true);mPendingCompactionProcesses.add(app);mCompactionHandler.sendMessage(mCompactionHandler.obtainMessage(COMPACT_PROCESS_MSG, app.mState.getSetAdj(), app.mState.getSetProcState()));}} else {if (DEBUG_COMPACTION) {Slog.d(TAG_AM, "Skipping full compaction for " + app.processName+ " oom adj score changed from " + app.mState.getSetAdj()+ " to " + app.mState.getCurAdj());}}}

如 3.1 节所述,这里多加了条件判断。

4. 压缩消息处理

消息处理的逻辑位于 MemCompactionHandler.handleMessage()函数,逻辑基本上与 Android R 版本相同,这里不做过多剖析。

需要注意的是两个地方:

  • 处理非system 的action时,最终调用 performCompaction() 函数;
  • 处理system 的action 时,最终调用的 compactSystem() 函数;

4.1 performCompaction()

        public void performCompaction(String action, int pid) throws IOException {if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FULL])) {compactProcess(pid, COMPACT_ACTION_FILE_FLAG | COMPACT_ACTION_ANON_FLAG);} else if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FILE])) {compactProcess(pid, COMPACT_ACTION_FILE_FLAG);} else if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_ANON])) {compactProcess(pid, COMPACT_ACTION_ANON_FLAG);}}

与Android R 版本不同,这里调用了 native 的接口:

static private native void compactProcess(int pid, int compactionFlags);
frameworks/base/services/core/jni/com_android_server_am_CachedAppOptimizer.cppstatic void com_android_server_am_CachedAppOptimizer_compactProcess(JNIEnv*, jobject, jint pid,jint compactionFlags) {compactProcessOrFallback(pid, compactionFlags);
}

---->

compactProcessOrFallback()

frameworks/base/services/core/jni/com_android_server_am_CachedAppOptimizer.cppstatic void compactProcessOrFallback(int pid, int compactionFlags) {// 入参compactionFlags 必须是ANON_FLAG 或 FILE_FLAG中的一种或两种if ((compactionFlags & (COMPACT_ACTION_ANON_FLAG | COMPACT_ACTION_FILE_FLAG)) == 0) return;// 确认是否有 anon 页回收,还是有file 页回收bool compactAnon = compactionFlags & COMPACT_ACTION_ANON_FLAG;bool compactFile = compactionFlags & COMPACT_ACTION_FILE_FLAG;// Set when the system does not support process_madvise syscall to avoid// gathering VMAs in subsequent calls prior to falling back to procfsstatic bool shouldForceProcFs = false;std::string compactionType;VmaToAdviseFunc vmaToAdviseFunc;// 根据压缩action,确认compactionType 和 回调函数//   回调函数后面会调用到,主要返回 process_madvise() 所需要的类型 MADV_COLD 或MADV_PAGEOUTif (compactAnon) {if (compactFile) {compactionType = "all";vmaToAdviseFunc = getAnyPageAdvice;} else {compactionType = "anon";vmaToAdviseFunc = getAnonPageAdvice;}} else {compactionType = "file";vmaToAdviseFunc = getFilePageAdvice;}// 如果compactProcess() 失败,即系统不支持 process_madvise()系统调用时,//    通过shouldForceProcFs 变量控制,强制走 proc/PID/reclaim 节点驱动if (shouldForceProcFs || compactProcess(pid, vmaToAdviseFunc) == -ENOSYS) {shouldForceProcFs = true;compactProcessProcfs(pid, compactionType);}
}

---->

compactProcess()

frameworks/base/services/core/jni/com_android_server_am_CachedAppOptimizer.cppstatic int64_t compactProcess(int pid, VmaToAdviseFunc vmaToAdviseFunc) {ProcMemInfo meminfo(pid);std::vector<Vma> pageoutVmas, coldVmas;auto vmaCollectorCb = [&coldVmas,&pageoutVmas,&vmaToAdviseFunc](const Vma& vma) {int advice = vmaToAdviseFunc(vma); //通过回调确定madvise的类型MADV_PAGEOUT或MADV_COLDswitch (advice) {case MADV_COLD:coldVmas.push_back(vma);break;case MADV_PAGEOUT:pageoutVmas.push_back(vma);break;}};// 读取 /proc/PID/maps 确定vma并调用回调函数 vmaClooectorCbmeminfo.ForEachVmaFromMaps(vmaCollectorCb);// 对进程中统计出来的 pageoutVma 进行 MADV_PAGEOUT 处理int64_t pageoutBytes = compactMemory(pageoutVmas, pid, MADV_PAGEOUT);if (pageoutBytes < 0) {// Error, just forward it.return pageoutBytes;}// 对进程中统计出来的 coldVma 进行MADV_COLD处理int64_t coldBytes = compactMemory(coldVmas, pid, MADV_COLD);if (coldBytes < 0) {// Error, just forward it.return coldBytes;}return pageoutBytes + coldBytes;
}

---->

compactMemory()

frameworks/base/services/core/jni/com_android_server_am_CachedAppOptimizer.cppstatic int64_t compactMemory(const std::vector<Vma>& vmas, int pid, int madviseType) {// UIO_MAXIOV is currently a small value and we might have more addresses// we do multiple syscalls if we exceed its maximumstatic struct iovec vmasToKernel[UIO_MAXIOV];if (vmas.empty()) {return 0;}unique_fd pidfd(pidfd_open(pid, 0));if (pidfd < 0) {// Skip compaction if failed to open pidfd with any errorreturn -errno;}int64_t totalBytesCompacted = 0;for (int iBase = 0; iBase < vmas.size(); iBase += UIO_MAXIOV) {int totalVmasToKernel = std::min(UIO_MAXIOV, (int)(vmas.size() - iBase));for (int iVec = 0, iVma = iBase; iVec < totalVmasToKernel; ++iVec, ++iVma) {vmasToKernel[iVec].iov_base = (void*)vmas[iVma].start;vmasToKernel[iVec].iov_len = vmas[iVma].end - vmas[iVma].start;}auto bytesCompacted =process_madvise(pidfd, vmasToKernel, totalVmasToKernel, madviseType, 0);if (CC_UNLIKELY(bytesCompacted == -1)) {return -errno;}totalBytesCompacted += bytesCompacted;}return totalBytesCompacted;
}

 最终使用 process_madvise() 对每个vma 进行处理。

4.2 compactSystem()

frameworks/base/services/core/jni/com_android_server_am_CachedAppOptimizer.cppstatic void com_android_server_am_CachedAppOptimizer_compactSystem(JNIEnv *, jobject) {std::unique_ptr<DIR, decltype(&closedir)> proc(opendir("/proc"), closedir);struct dirent* current;while ((current = readdir(proc.get()))) {if (current->d_type != DT_DIR) {continue;}// don't compact system_server, rely on persistent compaction during screen off// in order to avoid mmap_sem-related stallsif (atoi(current->d_name) == getpid()) {continue;}std::string status_name = StringPrintf("/proc/%s/status", current->d_name);struct stat status_info;if (stat(status_name.c_str(), &status_info) != 0) {// must be some other directory that isn't a pidcontinue;}// android.os.Process.FIRST_APPLICATION_UIDif (status_info.st_uid >= 10000) {continue;}int pid = atoi(current->d_name);compactProcessOrFallback(pid, COMPACT_ACTION_ANON_FLAG | COMPACT_ACTION_FILE_FLAG);}
}

最终调用的也是 compactProcessOrFallback() 函数。

5. process_madvise()

bionic/libc/include/sys/mman.hssize_t process_madvise(int picfd, const struct iovec* iovec, size_t vlen, int advice, unsigned flags);

process_madvise() 这个系统调用用于 kernel 关于进程地址区域的处理给出建议或方向。

需要支持 Linux 5.10 版本;

这些建议目的是为了提高系统或应用的性能。

地址区域通过结构体 iovec 和 vlen 管理。

参数:

  • pidfd:PID 的文件描述符,用于指定进程;
  • iovec:一个指向结构体数组的指针;
  • vlen:指定结构体数组的长度,这个值不能超过 IOV_MAX
  • advice:下面类型中的一种:
    • MADV_COLD,从Linux 5.4 开始,用以deactivate 给定的 range of pages;
    • MADV_COLLAPSE;
    • MADV_PAGEOUT,从 Linux 5.4 开始,用以 reclaim 给定的 range of pages;
    • MADV_WILLNEED;
  • flags:reserved,目前默认设0;

详细的内核调用这里就不过多剖析,都是调用 walk_page_range(),walk_ops 也都是 cold_walk_ops,但根据 advise 不同,逻辑略微偏差,就是 deactivate 和reclaim 逻辑。

 

 

至此,Android S 版本中关于 app 回收优化就全部剖析完成,下面做个总结:

  • 使用CachedAppOptimizer 对app compaction 进行管理,触发compact 接口主要分:
    • 当adj 发生变化时,如果上一次adj <= PERCEPTIBLE_APP_ADJ,变成 PREVIOUS HOME,使用 SOME  压缩优化;
    • 当adj 发生变化时,如果上一次非 CACHED,变成 CACHED,使用 FULL 压缩优化;
    • 当处于非唤醒状态,且当上一次的adj (setAdj) 重要性高于前台 adj (FOREGROUND_APP_ADJ),且距上一次压缩间隔超过10min 或从没有压缩过,使用 compactAppPersistent() 压缩优化;
    • 当处于非唤醒状态,且应用进程状态为 PROCESS_STATE_BOUND_FOREGROUND_SERVICE,且距上一次压缩间隔超过 10min 或从没有压缩过,使用 compactAppBfgs() 压缩优化;
    • AMS 中调用 finishBooting() 时,即系统启动完成后对系统所有进程进行全面压缩,调用 compactAllSystem()
    • MountServiceIdler 在每一次 startJob() 的时候调用 AMS.performIdleMaintenance(),compactAllSystem()进行系统压缩;
  • CachedAppOptimizer 中维护一个 ServiceThread,几种处理compact 调用后发出的消息;
  • 非 system 压缩,都需要经过很多限制条件之后,才能正式进入压缩处理函数;
  • 在压缩前会对 compact action 进行合并,SOME 使用的的是 mCompactActionSome 压缩方式,FULL / PERSISTENT / BFGS 使用的都是 mCompactActionFull 压缩方式;
  • 压缩处理函数分:
    • performCompaction(),调用 native 接口 compactProcess(),如果系统支持 process_madvise(),则使用 process_madvise() 来优化;如果不支持该系统调用,则采用 Android R 的机制,写 /proc/PID/reclaim 节点,需要驱动支持;
    • compactSystem(),这个是native 的调用,最终也是check 是否支持process_madvise(),同上;

关于Andoid R版本的压缩优化,可以查看上一篇博文

参考:

https://man7.org/linux/man-pages/man2/process_madvise.2.html

https://man7.org/linux/man-pages/man2/madvise.2.html

https://justinwei.blog.csdn.net/article/details/131591931

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/5030.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis数据结构 — Listpack

目录 listpack 结构 listpack 节点结构 quicklist 虽然通过控制 quicklistNode 结构里的压缩列表的大小或者元素个数&#xff0c;来减少连锁更新带来的性能影响&#xff0c;但是并没有完全解决连锁更新的问题。 于是&#xff0c;Redis 在 5.0 新设计一个数据结构叫 listpack…

(学习笔记)TCP基础知识

什么是TCP? TCP 是面向连接的、可靠的、基于字节流的传输层通信协议。 面向连接&#xff1a;一定是[一对一]才能连接&#xff0c;不能像UDP协议可以一个主机同时向多个主机发送消息&#xff0c;也就是一对多是无法做到的&#xff1b;可靠的&#xff1a;无论网络链路中出现了…

dede ckeditor编辑器让上传图片自动使用绝对地址显示

dedecms ckeditor编辑器让上传图片自动使用绝对地址显示&#xff0c;本教程适合织梦cms v57&#xff0c;其他版本未测试。由于我们网站为了更好更有效的解决seo方案&#xff0c;于是在考虑到发布文章的时候&#xff0c;上传的正文图片&#xff0c;是非绝对路径的&#xff0c;于…

mongodb集群搭建

下载地址&#xff1a; https://www.mongodb.com/try/download/community下载mongodb-linux-x86_64-rhel70-5.0.18 搭建集群 tar -zxvf mongodb-linux-x86_64-rhel70-5.0.18.tgz mkdir -p data/dp cd mongodb-linux-x86_64-rhel70-5.0.18 mkdir -p data/db mkdir log mkdir c…

AIGC之文本内容生成概述(下)——Transformer

在上一篇文章中&#xff0c;我们一口气介绍了LSTM、Word2Vec、GloVe、ELMo等四种模型的技术发展&#xff0c;以及每种模型的优缺点与应用场景&#xff0c;全文超过一万字&#xff0c;显得冗长且繁杂&#xff0c;在下文部分我们将分开介绍Transformer、BERT、GPT1/GPT2/GPT3/Cha…

Java:输入与输出

目录 输入输出args 输入Scanner 输入格式化输出文件输入与输出 输入输出 args 输入 利用main函数中的参数args&#xff0c;当然也可以起别的名字。其他语言也是一样的。输入时空格分隔。 args的作用&#xff1a;在程序启动时可以用来指定外部参数 Scanner 输入 需要import j…

机器学习深度确定性策略梯度(DDPG)笔记

深度确定性策略梯度&#xff08;Deep Deterministic Policy Gradient&#xff0c;DDPG&#xff09;是一种用于解决连续动作空间的强化学习算法。它结合了确定性策略梯度方法和深度神经网络。 DDPG算法的基本思想是通过两个神经网络来近似值函数和策略函数。其中&#xff0c;值…

JDK、JRE、JVM之间的关系是什么?

目录 JVM、JRE、JDK的关系&#xff1f; JDK、JRE、JVM都是什么&#xff1f; JVM JRE JDK JVM、JRE、JDK的关系&#xff1f; 三者包含关系&#xff1a; JDK>JRE>JVM JDK、JRE、JVM都是什么&#xff1f; jdk&#xff1a;是用于java开发的最小环境 包括&#xff1a;ja…

【webpack】webpack初步了解(存档自用)

2023/7/14 09:26:28 webpack工程化构建 2023/7/14 09:26:50 Webpack是一个现代化的前端工程化构建工具&#xff0c;它可以将多个模块打包成一个或多个静态资源文件。使用Webpack&#xff0c;你可以通过配置定义各种模块之间的依赖关系、加载器和插件。 以下是使用Webpack进…

如何助力企业DCMM贯标落地,这里有答案

DCMM作为国家第一个数据管理领域标准&#xff0c;是企业落实数据驱动战略、实现数字化转型的重要抓手。从行业实践来看&#xff0c;国内多个行业开始在全面拥抱DCMM模型&#xff0c;根据模型开展数据管理评估和能力提升工作。 01 什么是DCMM DCMM是国家标准《GB/T36073-2018 数…

Word文档突然无法打开?如何修复损坏文档?

在工作学习中&#xff0c;通常会遇到这种情况&#xff0c;我们正在编辑Word文件&#xff0c;电脑忽然断电关机&#xff0c;或者死机需要重启。当电脑重启以后&#xff0c;辛辛苦苦编辑很久的Word文件却忽然打不开了&#xff01;一直提示文件错误&#xff0c;如何解决Word无法打…

postgresql 内核源码分析 表锁relation lock的使用,session lock会话锁的应用场景,操作表不再困难

​专栏内容&#xff1a; postgresql内核源码分析 手写数据库toadb 并发编程 个人主页&#xff1a;我的主页 座右铭&#xff1a;天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物. 表锁介绍 当表打开&#xff0c;或者操作表时&#xff0c;都需要…

常考的技巧类算法题(一):下一个排列

技巧类算法题是我随口提的一个概念&#xff0c;意思就是这道题有自己独特的思考思路&#xff0c;仅仅知道它所涉及的最基础的知识点&#xff08;如宽泛的双指针&#xff0c;动规或深度优先搜索&#xff09;&#xff0c;然后自行推理解题套路十分困难。因此在此做一个整理&#…

Mybatis解析mybatis-config.xml过程

文章目录 1. 相关代码2. 加载资源3. 创建SqlSessionFactory3.1 整体创建过程3.2. 创建XMLConfigBuilder.3.2.1 XMLConfigBuilder 的核心字段3.2.2 BaseBuilder 3.3 创建Configuration3.4 全局配置文件解析过程3.4.1 parseConfiguration方法3.4.2 properties标签解析3.4.3 setti…

flink on yarn 中的flink-conf.yaml参数

在 Flink on YARN 中,flink-conf.yaml 是 Flink 配置文件,用于配置 Flink 应用程序在 YARN 上的运行。通过修改 flink-conf.yaml 文件中的参数,你可以调整 Flink 集群的行为和性能。以下是一些常见的在 flink-conf.yaml 中设置的参数: yarn.application.name: 指定 Flink 应…

Go速成-常量

1.常量的定义 Go语言常量&#xff0c;定义的值是不能进修修改的&#xff0c;定义常量就是const&#xff0c;常量定义建议全部大写 const PI float32 3.1415946 //显式定义 const (x int 16ys "abc"z)fmt.Print(x,y,s,z) 在定义常量的时候如果没有声明值&#xff…

深入理解Spring中的立即加载和延迟加载

引言 在使用Spring框架进行开发时&#xff0c;掌握加载策略是至关重要的。Spring框架提供了两种主要的加载策略&#xff1a;立即加载&#xff08;Eager Loading&#xff09;和延迟加载&#xff08;Lazy Loading&#xff09;。这两种加载策略在不同的场景下有各自的优势和适用性…

Gradle和Aritifactory,实现上传Apk并发送消息到钉钉

Gradle和Aritifactory 本文链接&#xff1a;https://blog.csdn.net/feather_wch/article/details/131746580 文章目录 Gradle和AritifactoryGradle基本介绍Gradle插件开发流程本地仓库artifactory搭建添加仓库使用本地仓库gradle插件仓库引入 插件buildSrc开发步骤xxxPluginPg…

五、DQL-2.基本查询

一、数据准备 1、删除表employee&#xff1a; drop table employee; 2、创建表emp&#xff1a; 3、添加数据&#xff1a; 4、查看表数据&#xff1a; 【代码】 -- 查询数据--------------------------------------------------------- drop table emp;-- 数据准备-----------…

linux之Ubuntu系列 find 、 ln 、 tar、apt 指令 软链接和硬链接 snap

查找文件 find 命令 功能非常强大&#xff0c;通常用来在 特定的目录下 搜索 符合条件的文件 find [path] -name “.txt” 记得要加 “ ” 支持通配符 &#xff0c;正则表达式 包括子目录 ls 不包括 子目录 如果省略路径&#xff0c;表示 在当前路径下&#xff0c;搜索 软链接…