我的编程空间,编程开发者的网络收藏夹
学习永远不晚

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

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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.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 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.cppstatic int64_t compactProcess(int pid, VmaToAdviseFunc vmaToAdviseFunc) {    ProcMemInfo meminfo(pid);    std::vector 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.cppstatic int64_t compactMemory(const std::vector& 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.cppstatic void com_android_server_am_CachedAppOptimizer_compactSystem(JNIEnv *, jobject) {    std::unique_ptr 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.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

来源地址:https://blog.csdn.net/jingerppp/article/details/131769953

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

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

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

在Go语言中实现高效的垃圾回收和内存优化

Go语言自带了一款高效的垃圾回收器(GC),能够自动管理内存的分配和回收,减轻了开发者的负担。不过,为了进一步优化内存的使用,开发者也可以采取一些措施。以下是一些实现高效垃圾回收和内存优化的方法:1. 使用适当的数据结构:选择合适的数据结构
2023-10-08

在Golang的高并发场景中如何优化内存分配和回收?

针对高并发场景中 golang 程序对内存分配和回收的优化,以下是四个主要技术:1. 内存池:预分配内存块,减少分配开销,消除碎片化。2. mmap:直接映射文件或资源到内存,提高访问速度。3. 对象池:预实例化对象,减少重复分配和析构。4
在Golang的高并发场景中如何优化内存分配和回收?
2024-05-10

探索Go语言中的内存优化技术与垃圾回收器管理

Go语言中的内存优化技术和垃圾回收器管理是为了提高程序性能和减少内存占用。1. 栈分配:Go语言使用栈进行变量的分配,栈上的内存分配和回收速度比堆上的内存分配和回收速度快。栈上的内存分配是通过将变量分配到固定大小的内存块上实现的,当函数调用
2023-10-08

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录