Android におけるアプリメモリリサイクルの最適化 (2): S バージョン

ベースとなるバージョン: Android S

0. 序文

Android Q には、フレームワーク側のアプリのメモリ リサイクル最適化ソリューションが追加されました。アプリの oom adj が具体的に変更されると、フレームワーク側がアプリケーションのメモリを処理します。バージョンが進化するにつれて、この部分の最適化作業は改善されており、著者はこの部分の最適化プロセスをAndroid RAndroid Sそれぞれについて詳細に分析します。

前回の記事ではAndroid R版について 詳しく分析しましたが、今回は引き続き Android S 版について分析していきます。

知らせ:

この記事で言及されている「圧縮」という言葉は、実際にはメモリ リサイクルの最適化を指しますが、それが匿名ページのリサイクルなのかファイル ページのリサイクルなのかが明らかになるのは、正確なロジックが明らかになったときだけです。処理。

さらに、この記事の分析は Android R バージョンと比較され、最後に 2 つのバージョンの違い、利点、欠点がまとめられます。

1. CachedAppOptimizer クラス

アプリ側のメモリ圧縮管理は CachedAppOptimizer クラスで完了します。

以前のブログ投稿「oom_adj 更新原則 (1)」および「oom_adj 更新原則 (2)」で、AMS が OomAdjuster クラスを通じて oom_adj の更新、計算、適用を管理していることを学びました。CachedAppOptimizer オブジェクトも OomAdjuster でインスタンス化されます。

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で構築内に作成されます。

mProcessDependency は DefaultProcessDependency タイプのオブジェクトで、最終的な圧縮処理に使用されます。

Android R との違い:

  • AMS のプロセス管理を同期するために、新しいmProcLock変数が追加されました。
  • cached_apps_freezer の変更を監視するために、新しいmSettingsObserver変数が追加されました 。
  • プロセスがフリーズしているときに /proc/locks 情報を表示するために、新しいmProcLocksReader変数が追加されました 。

2.init()

Android R バージョンとは異なり、Android R の init() 関数は、installSystemProviders() でSystemServer . startOtherServices() および mOomAdjuster . initSettings() が呼び出されたときに、AMS の installSystemProviders() 関数を呼び出すことによってトリガーされます。

Android S では、 installSystemProviders() は ContentProviderHelper .java で定義されます

CachedAppOptimizer .init () はOomAdjuster .initSettings() で呼び出さます

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 があります。

さらに、最後にさらに 2 つの更新関数があります。

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 と比較すると、ここではスレッドの優先順位が動的に構成され、優先順位は 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 を使用します。

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 という 2 つの制限値が追加されています。FULL で圧縮する場合、最後の oom_adj がキャッシュされないことが必要です。R バージョンでは、これらの条件判定は OomAdjuster.applyOomAdjLocked() 関数内に配置されますが、S バージョンではここに配置されます。著者は、この設計の本来の目的は、ユーザーが完全に圧縮された 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;
        }
    }

この 2 つの機能は Android S に追加されており、主に FULL 圧縮の判定に使用されます。

3. 圧縮最適化イニシエーター

圧縮の最適化はさまざまな状況でトリガーされます。CachedAppOptimizer では、主に次のような圧縮最適化イニシエーターが含まれます。

  • CompactAppSome()
  • CompactAppFull()
  • CompactAppPersistent()
  • コンパクトAppBfgs()
  • コンパクトオールシステム()

重要なロジックは Android R 版と同様ですが、この記事では主に最初の 2 つの機能を分析します。

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 での非キャッシュからキャッシュへの筆者は条件を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 バージョンと同じです。ここではあまり分析しません。

注意すべき点が 2 つあります。

  • 非システムアクションを処理する場合、最後に PerformCompaction() 関数が呼び出されます。
  • システムのアクションを処理するとき、最後に 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 バージョンとは異なり、ネイティブ インターフェイスはここで呼び出されます。

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);
}

---->

コンパクトプロセスまたはフォールバック()

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);
    }
}

---->

コンパクトプロセス()

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;
}

---->

コンパクトメモリ()

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;
}

 各 vma は最終的に process_madvise() を使用して処理されます。

4.2 コンパクトシステム()

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()システム コールは、プロセスアドレス領域の処理に関する提案や指示を与えるためにカーネルによって使用されます

Linux 5.10 バージョンをサポートする必要があります。

これらの提案は、システムまたはアプリケーションのパフォーマンスを向上させることを目的としています。

アドレス領域は構造体 iovec および vlen によって管理されます。

パラメータ:

  • pidfd: PID のファイル記述子。プロセスを指定するために使用されます。
  • iovec: 構造体配列へのポインタ。
  • vlen: 構造体配列の長さを指定します。この値はIOV_MAXを超えることはできません。
  • アドバイス: 次のタイプのいずれか:
    • Linux 5.4 以降の MADV_COLD は、指定された範囲のページを非アクティブ化するために使用されます。
    • MADV_COLLAPSE;
    • Linux 5.4 以降の MADV_PAGEOUT は、指定された範囲のページを再利用するために使用されます。
    • MADV_WILLNEED;
  • flags: 予約済み、現在デフォルトで 0 に設定されています。

詳細なカーネル呼び出しについてはここではあまり分析しませんが、すべて walk_page_range() を呼び出しており、walk_ops も cold_walk_ops ですが、アドバイスによってはロジックが少し異なり、非アクティブ化と再利用のロジックが含まれます。

 

 

この時点で、Android S バージョンにおけるアプリのリサイクル最適化の分析はすべて完了しました。概要は次のとおりです。

  • CachedAppOptimizer を使用してアプリの圧縮を管理し、次の主な方法でコンパクト インターフェイスをトリガーします。
    • adj が変更されたとき、最後の adj <=  PERCEPTIBLE_APP_ADJがPREVIOUSまたはHOMEになった場合は、何らかの圧縮最適化を使用します。
    • adj が変更されると、前回 CACHED されていなかった場合は CACHED になり、完全な圧縮最適化が使用されます。
    • 非覚醒状態で、最後の adj (setAdj) が前景 adj ( FOEGROUND_APP_ADJ ) よりも重要で、最後の圧縮からの間隔が 10 分を超えるか、一度も圧縮されていない場合は、  compactAppPersistent() 圧縮を使用します。最適化;
    • 非覚醒状態で、アプリケーション プロセスのステータスが PROCESS_STATE_BOUND_FOREGROUND_SERVICEで、最後の圧縮からの間隔が 10 分を超えているか、一度も圧縮されていない場合は、  compactAppBfgs() 圧縮最適化を使用します。
    • AMS でfinishBooting() が呼び出されるとき、つまりシステムの起動が完了した後、システム内のすべてのプロセスが完全に圧縮され、compactAllSystem()が呼び出されます。
    • MountServiceIdler は、 startJob() がシステム圧縮を実行するたびに、AMS .performIdleMaintenance() およびCompactAllSystem()を呼び出します。
  • CachedAppOptimizer は、compact の呼び出し後に送信されたメッセージを処理するために ServiceThread を維持します。
  • 非システム圧縮では、正式に圧縮処理機能に入る前に多くの制限が必要です。
  • コンパクト アクションは圧縮前にマージされます。一部では mCompactActionSome 圧縮方法が使用され、FULL / PERSISTENT / BFGS はすべて mCompactActionFull 圧縮方法を使用します。
  • 圧縮処理機能のポイント:
    • PerformCompaction()、ネイティブ インターフェイスCompactProcess()を呼び出します。システムが process_madvise() をサポートしている場合は、最適化にprocess_madvise()を使用します。システム コールがサポートされていない場合は、Android R メカニズムを使用して /proc/PID/reclaim を書き込みますドライバーのサポートが必要なノード。
    • CompactSystem()、これはネイティブ呼び出しであり、上記と同様に、最終的に 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

おすすめ

転載: blog.csdn.net/jingerppp/article/details/131769953