Android OOM Adjustments

22290 ワード

以前、LMKを分析する文章を書いたことがありますが、それは主にLMKの実現原理を分析し、AMSのOOM Adjの調整を詳しく分析していません.今回はAndroid 9.0のコードを参考に分析してみましたが、主にコード実装を分析しています.まずOOM Adjの定義を見てみましょう.
Adj
Value
Comments
UNKNOWN_ADJ
1001
特定できないAdjは、通常キャッシュするプロセスです.
CACHED_APP_MAX_ADJ
906
非表示プロセスのAdj最大値
CACHED_APP_MIN_ADJ
900
非表示プロセスのAdj最小値
SERVICE_B_ADJ
800
Bリストのサービスは、Aリストよりユーザーへの接着度が小さい
PREVIOUS_APP_ADJ
700
ユーザの前回のインタラクションのプロセス
HOME_APP_ADJ
600
Launcherプロセス
SERVICE_ADJ
500
サービスプロセスの適用
HEAVY_WEIGHT_APP_ADJ
400
バックグラウンドのヘビー級プロセス
BACKUP_APP_ADJ
300
backup関連操作を担持するプロセス
PERCEPTIBLE_APP_ADJ
200
バックグラウンド音楽の再生など、感知可能なプロセス
VISIBLE_APP_ADJ
100
フロントに表示されるActivityプロセス
FOREGROUND_APP_ADJ
0
現在フロントで実行されているプロセス、つまりユーザーが対話しているプログラム
PERSISTENT_SERVICE_ADJ
-700
システムプロセスまたはPersistentプロセスにバインドされたプロセス
PERSISTENT_PROC_ADJ
-800
Persistentプロパティのプロセス、例えばtelephony
SYSTEM_ADJ
-900
システムプロセス
NATIVE_ADJ
-1000
Nativeプロセス、システム管理されない
updateOomAdj
OOM Adjの更新はAMSにおけるupdateOomAdjLocked()関数により行われる.この関数ではOOM Adjだけでなくメモリの調整も行います.まずOOM Adjの調整実装を見てみましょう.
    final void updateOomAdjLocked() {
        ......
        boolean retryCycles = false;

        //   service  ,       cycle  ,
        for (int i=N-1; i>=0; i--) {
            ProcessRecord app = mLruProcesses.get(i);
            app.containsCycle = false;
        }
        for (int i=N-1; i>=0; i--) {
            ProcessRecord app = mLruProcesses.get(i);
            if (!app.killedByAm && app.thread != null) {
                app.procStateChanged = false;
                //   app OOM adj
                computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now);
                
                //           cycle ,        
                retryCycles |= app.containsCycle;
                
                //       Adj       ,       。
                if (app.curAdj >= ProcessList.UNKNOWN_ADJ) {
                    ......
                }
            }
        }
        
        //     cycle     ,    OOM Adj,           
        int cycleCount = 0;
        while (retryCycles) {
            cycleCount++;
            retryCycles = false;

            for (int i=0; i=0; i--) {
            ProcessRecord app = mLruProcesses.get(i);
            if (!app.killedByAm && app.thread != null) {
                //      OOM Adj
                applyOomAdjLocked(app, true, now, nowElapsed);
                
                //            ,               empty  
                switch (app.curProcState) {
                    case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
                    case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
                           ......
                        break;
                    case ActivityManager.PROCESS_STATE_CACHED_EMPTY:
                        ......
                        break;
                    default:
                        mNumNonCachedProcs++;
                        break;
                }

                if (app.isolated && app.services.size() <= 0 && app.isolatedEntryPoint == null) {
                    //       
                    app.kill("isolated not needed", true);
                } else {
                    //      ,  uid
                    final UidRecord uidRec = app.uidRecord;
                    ......
                }

                //         HOME   ,          
                if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME
                        && !app.killedByAm) {
                    numTrimming++;
                }
            }
        }

        //      uid                 ,           
        incrementProcStateSeqAndNotifyAppsLocked();

        mNumServiceProcs = mNewNumServiceProcs;
        ......
        //          
        ......
        //           Activity
        if (mAlwaysFinishActivities) {
            mStackSupervisor.scheduleDestroyAllActivities(null, "always-finish");
        }
               
        if (allChanged) {
            requestPssAllProcsLocked(now, false, mProcessStats.isMemFactorLowered());
        }
        
        ArrayList becameIdle = null;

        //   UidRecord
        if (mLocalPowerManager != null) {
            mLocalPowerManager.startUidChanges();
        }
        for (int i=mActiveUids.size()-1; i>=0; i--) {
            ......
        }
        if (mLocalPowerManager != null) {
            mLocalPowerManager.finishUidChanges();
        }
        ......
    }

OOM Adjの更新プロセスは主に以下の作業を完了します.
  • プロセスのOOM Adj値を再計算し、更新する.
  • Adj値が割り当てられていないプロセスは、プロセスステータスに応じてバックグラウンドキャッシュプロセスとemptyプロセスに分けられ、CACHED_APP_MIN_ADJからCACHED_APP_MAX_ADJは、Adj値を割り当てる.バックグラウンドキャッシュプロセスとemptyプロセスのAdj値は交差して増加し、各レベルのプロセス個数は予め計算された最大値を超えない.
  • LRUのプロセスを逆処理し、制限を超えるバックグラウンドキャッシュプロセスとemptyプロセスを回収します.デフォルトの制限では、バックグラウンドキャッシュプロセスとemptyプロセスがそれぞれ16個ずつあります.
  • isolatedプロセス結果にはサービスが含まれておらず、直接回収されています.
  • プロセスのUIDERecordを更新します.

  • 次にupdateOomAdjLocked()のメモリ調整に関する部分を分析します.Androidはシステムメモリの状態を4段階に分け、以下のように定義しています.
    Adj
    Value
    Comments
    ADJ_MEM_FACTOR_NORMAL
    0
    システムメモリは正常で、調整する必要はありません
    ADJ_MEM_FACTOR_MODERATE
    1
    システムメモリは中程度で、正常な状態より低い
    ADJ_MEM_FACTOR_LOW
    2
    システムメモリが低いので、メモリを回収する必要があります.
    ADJ_MEM_FACTOR_CRITICAL
    3
    システムのメモリが不足しているので、メモリを回収しなければなりません.
    ComponentCallbacks 2では、メモリ回収のレベルも定義されています.最初の3つはバックグラウンドキャッシュの回収レベル、後の3つはプロセス実行時の回収レベルです.
    Adj
    Value
    Comments
    TRIM_MEMORY_COMPLETE
    80
    バックグラウンドLRUリストの末尾にあるプロセスは、より多くのメモリが見つからない場合、すぐに殺されます.
    TRIM_MEMORY_MODERATE
    60
    バックグラウンドLRUリストの中央にあるプロセスでは、メモリをクリーンアップすることで、後続のプロセスのパフォーマンスが向上します.
    TRIM_MEMORY_BACKGROUND
    40
    バックグラウンドプロセスは、LRUリストの先頭にあり、メモリをクリーンアップすることで、プロセスをより効率的にフロントに戻すことができます.
    TRIM_MEMORY_UI_HIDDEN
    20
    プロセスUIが表示されなくなり、UIリソースを解放できます.
    TRIM_MEMORY_RUNNING_CRITICAL
    15
    デバイスは低メモリで動作しており、バックグラウンドプロセスの生存を保証できません.重要でないリソースをできるだけ解放する必要があります.次にonLowMemory()を呼び出してシステムのメモリが低く、ユーザーに顕著な影響を及ぼしていることを報告します.
    TRIM_MEMORY_RUNNING_LOW
    10
    デバイスが低メモリで動作しているため、不要なリソースを解放する必要があります.
    TRIM_MEMORY_RUNNING_MODERATE
    5
    デバイスの動作メモリが低いため、不要なリソースを解放する必要がある場合があります.
    Androidシステムは、バックグラウンドキャッシュプロセスとemptyプロセスの数に応じてメモリレベルを区別します.システムは常にバックグラウンドプロセスをできるだけ多く保持しているため、プロセスの再起動時に起動時間を減らすことができ、ユーザー体験がより良い.しかし,システムメモリが不足するとlowmemeorykillerメカニズムが重要でないバックグラウンドプロセスを優先的に殺すため,バックグラウンドプロセスの数はlowmemrorykillerのトリガに関連していると考えられる.残りのバックグラウンドプロセスが少ないほど、Lowmemroykillerによって回収されるメモリが多いほど、システム全体のメモリが緊張することを示します.
        final void updateOomAdjLocked() {
            ......
            //        empty     
            final int numCachedAndEmpty = numCached + numEmpty;
            int memFactor;
            //   cache   empty         TRIM  ,            
            //      CUR_TRIM_EMPTY_PROCESSES=8,CUR_TRIM_CACHED_PROCESSES=5
            if (numCached <= mConstants.CUR_TRIM_CACHED_PROCESSES
                    && numEmpty <= mConstants.CUR_TRIM_EMPTY_PROCESSES) {
                if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) {
                    //  cache+empty     3 ,        
                    memFactor = ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
                } else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) {
                    //  cache+empty     5 ,       
                    memFactor = ProcessStats.ADJ_MEM_FACTOR_LOW;
                } else {
                    //           
                    memFactor = ProcessStats.ADJ_MEM_FACTOR_MODERATE;
                }
            } else {
                memFactor = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
            }
            ......
            mLastMemoryLevel = memFactor;
            mLastNumProcesses = mLruProcesses.size();
            //         ,      true
            boolean allChanged = mProcessStats.setMemFactorLocked(memFactor, !isSleepingLocked(), now);
            final int trackerMemFactor = mProcessStats.getMemFactorLocked();
            //           ,      
            if (memFactor != ProcessStats.ADJ_MEM_FACTOR_NORMAL) {
                if (mLowRamStartTime == 0) {
                    mLowRamStartTime = now;
                }
                int step = 0;
                int fgTrimLevel;
                //         fgTrimLevel, ComponentCallbacks2  
                switch (memFactor) {
                    ......
                }
                //   factory,    trimLevel     
                int factor = numTrimming/3;
                int minFactor = 2;
                if (mHomeProcess != null) minFactor++;
                if (mPreviousProcess != null) minFactor++;
                if (factor < minFactor) factor = minFactor;
                //    trimLevel,   
                int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
                //     LRU      
                for (int i=N-1; i>=0; i--) {
                    ProcessRecord app = mLruProcesses.get(i);
                    ......
                    //           ,      HOME
                    if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME
                            && !app.killedByAm) {
                        //    trimLevel      ,     
                        if (app.trimMemoryLevel < curLevel && app.thread != null) {
                            try {
                                app.thread.scheduleTrimMemory(curLevel);
                            } catch (RemoteException e) {
                            }
                            ......
                        }
                        //      trimLevel,  factor      :COMPLETE->MODERATE->BACKGROUND
                        app.trimMemoryLevel = curLevel;
                        step++;
                        if (step >= factor) {
                            ......
                        }
                    } else if (app.curProcState == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
                            && !app.killedByAm) {
                        // heavy weight   TRIM_MEMORY_BACKGROUND     
                        if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND
                                && app.thread != null) {
                            try {
                                app.thread.scheduleTrimMemory(
                                        ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
                            } catch (RemoteException e) {
                            }
                        }
                        app.trimMemoryLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
                    } else {
                        //          UI , TRIM_MEMORY_UI_HIDDEN    
                        if ((app.curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
                                || app.systemNoUi) && app.pendingUiClean) {
                            final int level = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN;
                            if (app.trimMemoryLevel < level && app.thread != null) {
                                try {
                                    app.thread.scheduleTrimMemory(level);
                                } catch (RemoteException e) {
                                }
                            }
                            app.pendingUiClean = false;
                        }
                        //  fgTrimLevel    trimLevel , fgTrimLevel    
                        if (app.trimMemoryLevel < fgTrimLevel && app.thread != null) {
                            try {
                                app.thread.scheduleTrimMemory(fgTrimLevel);
                            } catch (RemoteException e) {
                            }
                        }
                        app.trimMemoryLevel = fgTrimLevel;
                    }
                }
            } else {
                //         
                if (mLowRamStartTime != 0) {
                    mLowRamTimeSinceLastIdle += now - mLowRamStartTime;
                    mLowRamStartTime = 0;
                }
                for (int i=N-1; i>=0; i--) {
                    ProcessRecord app = mLruProcesses.get(i);
                    if (allChanged || app.procStateChanged) {
                        setProcessTrackerStateLocked(app, trackerMemFactor, now);
                        app.procStateChanged = false;
                    }
                    //     UI    TRIM_MEMORY_UI_HIDDEN    
                    if ((app.curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
                            || app.systemNoUi) && app.pendingUiClean) {
                        if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
                                && app.thread != null) {
                            try {
                                app.thread.scheduleTrimMemory(
                                        ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
                            } catch (RemoteException e) {
                            }
                        }
                        app.pendingUiClean = false;
                    }
                    app.trimMemoryLevel = 0;
                }
            }
            ......
        }

    メモリ調整の主な仕事は、プロセスを殺さずに、必要に応じてメモリを回収することです.
  • メモリが低い場合、プロセス状態がHOMEより大きい重要でないプロセスでは、LRUの逆順序メモリ回収レベルに応じて徐々に低下します.
  • メモリが低い場合、重要なプロセスほど重要なメモリ回収レベルが高くなります.
  • メモリが正常であれば、バックグラウンドにUIがあるプロセスのメモリ回収を行います.

  • computeOomAdj
    上でupdateOomAdjLocked()の大まかな流れを分析し、次にその中の重要な関数computeOomAdjLocked()の1つを分析し、OOM Adjをどのように計算するかを分析します.
        private final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj,
                ProcessRecord TOP_APP, boolean doingAll, long now) {
            //   Adj   ,              
            if (mAdjSeq == app.adjSeq) {
                ......
            }
            
            //       Adj
            if (app.thread == null) {
                ......
            }
            ......
            //   Adj     FOREGROUND   Adj,     Persistent  
            if (app.maxAdj <= ProcessList.FOREGROUND_APP_ADJ) {
                //   Adj,SchedGroup,ProcState,UI   
                ......
                app.curAdj = app.maxAdj;
                app.completedAdjSeq = app.adjSeq;
                //   Adj       ,   Adj   
                return app.curAdj < prevAppAdj;
            }
            ......
            //             Adj,SchedGroup,ProcState
            if (PROCESS_STATE_CUR_TOP == ActivityManager.PROCESS_STATE_TOP && app == TOP_APP) {
                //     
                ......
            } else if (app.runningRemoteAnimation) {
                //          
                ......
            } else if (app.instr != null) {
                //         
                ......
            } else if (isReceivingBroadcastLocked(app, mTmpBroadcastQueue)) {
                //       
                ......
            } else if (app.executingServices.size() > 0) {
                //     Service   
                ......
            } else if (app == TOP_APP) {
                //     ,     
                ......
            } else {
                //    
                ......
            }
            
            //     activities,    Adj
            if (!foregroundActivities && activitiesSize > 0) {
                int minLayer = ProcessList.VISIBLE_APP_LAYER_MAX;
                for (int j = 0; j < activitiesSize; j++) {
                    ......
                    if (r.visible) {
                        //         activity,Adj     ,  VISIBLE
                        ......
                    } else if (r.isState(ActivityState.PAUSING, ActivityState.PAUSED)) {
                        //     activity      ,Adj  PERCEPTIBLE
                        ......
                    } else if (r.isState(ActivityState.STOPPING)) {
                        //     activity        ,Adj  PERCEPTIBLE
                        ......
                    } else {
                        //        cached-activity,   procState
                        ......
                    }
                }
                //        activity   Adj      ,   Adj  
                if (adj == ProcessList.VISIBLE_APP_ADJ) {
                    adj += minLayer;
                }
            }
            ......
            if (adj > ProcessList.PERCEPTIBLE_APP_ADJ
                    || procState > ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
                //            overylay UI   ,Adj   PERCEPTIBLE, procState  
                ......
            }
            
            if (adj > ProcessList.PERCEPTIBLE_APP_ADJ
                    || procState > ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) {
                //     toasts,Adj  PERCEPTIBLE
                ......
            }
            
            if (app == mHeavyWeightProcess) {
                //      ,Adj  HEAVY_WEIGHT
                ......
            }
            
            if (app == mHomeProcess) {
                // home  ,Adj  HOME
                ......
            }
            
            if (app == mPreviousProcess && app.activities.size() > 0) {
                //        ,Adj  PREVIOUS
                ......
            }
            ......
            if (mBackupTarget != null && app == mBackupTarget.app) {
                //          ,       ,Adj  BACKUP
                ......
            }
            ......
            //   service Adj
            for (int is = app.services.size()-1;
                    is >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
                            || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
                            || procState > ActivityManager.PROCESS_STATE_TOP);
                    is--) {
                ServiceRecord s = app.services.valueAt(is);
                if (s.startRequested) {
                    //       Unbounded Service 
                    ......
                }
                
                for (int conni = s.connections.size()-1;
                        conni >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
                                || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
                                || procState > ActivityManager.PROCESS_STATE_TOP);
                        conni--) {
                    //       Bounded Service 
                    ......
                }
            }
    
            //     ContentProvider   
            for (int provi = app.pubProviders.size()-1;
                    provi >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
                            || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
                            || procState > ActivityManager.PROCESS_STATE_TOP);
                    provi--) {
                ContentProviderRecord cpr = app.pubProviders.valueAt(provi);
                ......
            }
     
            //       ContentProvider        ,Adj  PREVIOU
            if (app.lastProviderTime > 0 &&
                    (app.lastProviderTime+mConstants.CONTENT_PROVIDER_RETAIN_TIME) > now) {
                ......
            }
            
            //   services providers      top  ,     
            if (mayBeTop && procState > ActivityManager.PROCESS_STATE_TOP) {
                ......
            }
            
            // Cache  ,     
            if (procState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
                ......
            }
            
            //  service       
            if (adj == ProcessList.SERVICE_ADJ) {
                ......
            }
            ......
            return app.curAdj < prevAppAdj;
        }

    computeOomAdjLocked()はコード量が多く,論理が非常に複雑である.簡単に言えば,プロセスの様々な状態に応じて,Adj,schedGroup,procStateなどを調整する.ここは簡単に大まかな流れを引いただけで、細部は書いていません.多すぎます.
    applyOomAdj
    プロセスのOOM Adjを計算した後、Adj値をAndroid LowMemoryKiller(LMK)メカニズムに設定する必要があります.具体的なソースコードは以下の通りです.
        private final boolean applyOomAdjLocked(ProcessRecord app, boolean doingAll, long now,
                long nowElapsed) {
            ......
            if (app.curAdj != app.setAdj) {
                //  curAdj   LMK   ,Adj            Proc   
                ProcessList.setOomAdj(app.pid, app.uid, app.curAdj);
                app.setAdj = app.curAdj;
                app.verifiedAdj = ProcessList.INVALID_ADJ;
            }
            
            if (app.setSchedGroup != app.curSchedGroup) {
                int oldSchedGroup = app.setSchedGroup;
                app.setSchedGroup = app.curSchedGroup;
                if (app.waitingToKill != null && app.curReceivers.isEmpty()
                        && app.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND) {
                    //             ,    
                    app.kill(app.waitingToKill, true);
                    success = false;
                } else {
                    ......
                    try {
                        //        Group
                        setProcessGroup(app.pid, processGroup);
                        if (app.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
                            //      Group  TOP  TOP ,  UI   Render        。
                            //     RT    ,                 -10。
                            ......
                        } else if (oldSchedGroup == ProcessList.SCHED_GROUP_TOP_APP &&
                                   app.curSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
                            //      Group TOP   TOP ,  UI   Render        。
                            //          SCHED_OTHER,        0。
                            ......
                        } catch (Exception e) {
                        ......
                    }
                }
                //           activities ProcessState   
                ......
                if (app.setProcState == ActivityManager.PROCESS_STATE_NONEXISTENT
                    || ProcessList.procStatesDifferForMem(app.curProcState, app.setProcState)) {
                    //                     PSS      
                    ......
                } else {
                    //         ,  PSS  
                    ......
                }
                if (app.setProcState != app.curProcState) {
                    //       
                    ......
                } else if (app.reportedInteraction && (nowElapsed-app.interactionEventTime)
                    > mConstants.USAGE_STATS_INTERACTION_INTERVAL) {
                    //             ,            
                    maybeUpdateUsageStatsLocked(app, nowElapsed);
                }
    
                //   activities           ,       。
                if (changes != 0) {
                    ......
                }
    
            return success;
        }

    applyOomAdjLocked()はLMKのAdj値を設定するだけでなく、TOPプロセスのスケジューリングポリシーや優先度の調整、PSSデータの収集、ステータス変更ブロードキャストの送信などの作業も完了している.