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

这篇具有很好参考价值的文章主要介绍了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.java

    OomAdjuster(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.java

    public CachedAppOptimizer(ActivityManagerService am) {
        this(am, null, new DefaultProcessDependencies());
    }

    @VisibleForTesting
    CachedAppOptimizer(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.java

    public 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());
        }
 
        @Override
        public 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 compaction
            if (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 state
                if (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.cpp

static 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.cpp

static 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 procfs
    static bool shouldForceProcFs = false;
    std::string compactionType;
    VmaToAdviseFunc vmaToAdviseFunc;

    // 根据压缩action,确认compactionType 和 回调函数
    //   回调函数后面会调用到,主要返回 process_madvise() 所需要的类型 MADV_COLD 或MADV_PAGEOUT
    if (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.cpp

static 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_COLD
        switch (advice) {
            case MADV_COLD:
                coldVmas.push_back(vma);
                break;
            case MADV_PAGEOUT:
                pageoutVmas.push_back(vma);
                break;
        }
    };

    // 读取 /proc/PID/maps 确定vma并调用回调函数 vmaClooectorCb
    meminfo.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.cpp

static 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 maximum
    static 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 error
        return -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.cpp

static 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 stalls
        if (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 pid
            continue;
        }

        // android.os.Process.FIRST_APPLICATION_UID
        if (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.h

ssize_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文章来源地址https://www.toymoban.com/news/detail-594430.html

到了这里,关于Android 中app内存回收优化(二):S 版本的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 可测含多进程的app-- python调用adb命令获取Android App应用的性能数据:CPU、GPU、内存、电池、耗电量(含python源码)

    python脚本通过os.popen()方法运行adb命令,获取性能数据,将性能数据保存在csv文件并算出均值、最大值、最小值。 本脚本可测试一个app包含多个进程的场景,可以获取 每个进程的性能数据 。 2.1 软件环境 具备python环境,Android环境 需要python库:os, csv, time, datetime, sys,time,panda

    2024年02月13日
    浏览(44)
  • Android安卓实战项目(12)—关于身体分析,BMI计算,喝水提醒,食物卡路里计算APP【支持中英文切换】生活助手类APP(源码在文末)

    B站演示 【Android安卓实战项目(12)—生活助手类APP—关于身体分析,BMI计算,喝水提醒,食物卡路里计算APP【支持中英文切换】】 https://www.bilibili.com/video/BV1Wu4y1C76j/?share_source=copy_webvd_source=b2e9b9ed746acda34f499009647748ed 这段代码是一个Android应用程序的主要活动(Activity),它是一

    2024年02月10日
    浏览(46)
  • 【Android】使用对象池(Object Pool)来缓存已经创建的字节数组,避免频繁地进行内存分配和回收操作提高性能

    在Android中,使用new byte[]创建字节数组是在堆上分配内存,不会直接导致Native内存的增长。但是,如果我们频繁地创建和销毁字节数组,就可能会导致堆内存不足,并触发GC,从而影响应用程序的性能。 在Android中,堆内存的大小是有限制的。如果我们频繁地创建和销毁字节数

    2024年02月09日
    浏览(52)
  • 【Android 性能优化:内存篇】——WebView 内存泄露治理

    背景:笔者在公司项目中优化内存泄露时发现WebView 相关的内存泄露问题非常经典,一个 Fragment 页面使用的 WebView 有多条泄露路径,故记录下。 项目中一个Fragment 使用 Webview,在 Fragment onDestroyView 时候却没有释放,释放 WebView 还不简单嘛,于是笔者在 Fragment 的 onDestroyView 补充

    2024年02月04日
    浏览(45)
  • Android 性能优化——APP启动优化

            首先在《Android系统和APP启动流程》中我们介绍了 APP 的启动流程,但都是 FW 层的流程,这里我们主要分析一下在 APP 中的启动流程。要了解 APP 层的启动流程,首先要了解 APP 启动的分类。 冷启动         应用从头开始启动,即应用的首次启动。需要做大量的工

    2024年04月12日
    浏览(47)
  • 【Android内存优化】内存泄露优化之强引用变弱引用完全详解

    内存空间使用完毕后无法被释放的现象,对于还保持着引用, 该内存不能再被分配使用,逻辑上却已经不会再用到的对象,垃圾回收器不会回收它们。 所以逻辑不再使用的对象,需要释放强引用,以便GC进行回收。 JVM 垃圾回收原理,点击进入 常见Handler 写法 This Handler clas

    2024年02月08日
    浏览(47)
  • android源码学习- APP启动流程(android12源码)

    百度一搜能找到很多讲APP启动流程的,但是往往要么就是太老旧(还是基于android6去分析的),要么就是不全(往往只讲了整个流程的一小部分)。所以我结合网上现有的文章,以及源码的阅读和调试,耗费了3整天的时间,力求写出一篇最完整,最详细,最通俗易懂的文章,

    2024年02月11日
    浏览(46)
  • Android 开发的五大开源网站,安卓内存优化面试

    (4) 多快捷键支持 ① 左右翻页 在项目(搜索)列表及详情页左手党可以通过 awsd,右手党可通过上下左右键或者 nl 键翻页,浏览项目从未有过的流畅体验。 ② 快速打标签 项目详情页可通过 t 快速进入新增标签输入框,回车确定标签,Esc 退出编辑。 ③ 快速搜索 项目列表页可通

    2024年04月09日
    浏览(59)
  • 【Android】APP启动优化学习笔记

    用户体验: 应用的启动速度直接影响用户体验。用户希望应用能够快速启动并迅速响应他们的操作。如果应用启动较慢,用户可能会感到不满,并且有可能选择卸载或切换到竞争对手的应用。通过启动优化,可以提高应用的启动速度,让用户获得更好的使用体验。 竞争优势

    2024年02月14日
    浏览(41)
  • 【Android】app应用内版本更新升级(DownloadManager下载,适配Android6.0以上所有版本)

    版本的升级和更新是一个线上App所必备的功能,App的升级安装包主要通过 应用商店 或者 应用内下载 两种方式获得,大部分app这两种方式都会具备,应用商店只需要上传对应平台审核通过即可,而应用内更新一般是通过以下几种方式: 1.集成第三方库如 appupdateX、bugly 的更新

    2024年02月11日
    浏览(115)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包