Android memory trim

本文基于 AOSP android-8.1.0_r31

Android Low memory killer 已經(jīng)分析了低殺的情況傻谁。低殺意味著緩存的數(shù)量過多了老厌,或者內(nèi)存已經(jīng)出現(xiàn)了不足的情況温算。盡管經(jīng)過了低殺了襟锐,系統(tǒng)的內(nèi)存也可能隨時都會出現(xiàn)緊張的情況蒲列,那么此時比較好的作法就是通知當(dāng)前沒有被殺掉的進(jìn)程忽媒,讓這些進(jìn)程主動去釋放一些內(nèi)存争拐。否則下次進(jìn)行低殺的時候,這些進(jìn)程就可能會被殺掉猾浦。這樣的話陆错,這些進(jìn)程為了自保,也會被動的愿意去釋放一些不用的內(nèi)存金赦。這樣一來系統(tǒng)內(nèi)存就充足了音瓷,就不會低殺了。
所以一個App開發(fā)者是很有必要去實現(xiàn) onTrimMemory

Memory Trim發(fā)生的時機

Android Low memory killerupdateOomAdjLocked在計算每個進(jìn)程的adj, 以及可能的低殺后夹抗,就會試著去通知app trim memory.

        // Now determine the memory trimming level of background processes.
        // Unfortunately we need to start at the back of the list to do this
        // properly.  We only do this if the number of background apps we
        // are managing to keep around is less than half the maximum we desire;
        // if we are keeping a good number around, we'll let them use whatever
        // memory they want.
        final int numCachedAndEmpty = numCached + numEmpty;
        int memFactor;
        if (numCached <= mConstants.CUR_TRIM_CACHED_PROCESSES
                && numEmpty <= mConstants.CUR_TRIM_EMPTY_PROCESSES) {
            if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) {
                memFactor = ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
            } else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) {
                memFactor = ProcessStats.ADJ_MEM_FACTOR_LOW;
            } else {
                memFactor = ProcessStats.ADJ_MEM_FACTOR_MODERATE;
            }
        } else {
            memFactor = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
        }

計算當(dāng)前內(nèi)存因子绳慎,也就是當(dāng)前內(nèi)存的緊張程度。值越大漠烧,內(nèi)存越緊張杏愤。

這里特別注意,內(nèi)存因子是根據(jù)cached/empty進(jìn)程的數(shù)量來計算的已脓。

變量 默認(rèn)值
CUR_TRIM_CACHED_PROCESSES 5
CUR_TRIM_EMPTY_PROCESSES 8
TRIM_CRITICAL_THRESHOLD 3
TRIM_LOW_THRESHOLD 5

也就是說

cached和empty總數(shù) 系統(tǒng)內(nèi)存狀態(tài)
0 ~ 3 critical
4, 5 low
6 ~ 13 moderate
> 13 normal

這些數(shù)值好像與我們平時理解的剛好相反珊楼,比如當(dāng)cached/empty的進(jìn)程更多時,那此時系統(tǒng)不應(yīng)該內(nèi)存更緊張么度液?因為這些緩存的進(jìn)程并沒有完全釋放完內(nèi)存呀厕宗。
但是此時內(nèi)存因子確為normal狀態(tài)画舌, 是不是很奇怪。參考https://www.cnblogs.com/tiger-wang-ms/p/6445213.html

為什么能根據(jù)后臺進(jìn)程和空進(jìn)程數(shù)量來判斷出系統(tǒng)的內(nèi)存等級呢已慢?因為根據(jù)之前的分析可以知道曲聂,Android系統(tǒng)在后臺進(jìn)程和空進(jìn)程不超過數(shù)量上限時總是盡可能多的保留后臺進(jìn)程和空進(jìn)程,這樣用戶便可再再次啟動這些進(jìn)程時減少啟動時間從而提高了用戶體驗佑惠;而lowmemeorykiller的機制又會在系統(tǒng)可用內(nèi)存不足時殺死這些進(jìn)程朋腋,所以在后臺進(jìn)程和空進(jìn)程數(shù)量少于一定數(shù)量時,便表示了系統(tǒng)以及觸發(fā)了lowmemrorykiller的機制膜楷,而剩余的后臺進(jìn)程和空進(jìn)程的數(shù)量則正好體現(xiàn)了Lowmemroykiller殺進(jìn)程的程度旭咽,即表示當(dāng)前系統(tǒng)內(nèi)存的緊張程度。

那這里有個問題把将,如果系統(tǒng)剛開機時轻专,用戶并沒有操作過其它app, 那么此時系統(tǒng)的cached/empty的進(jìn)程豈不是為0,那這時如果啟動一個app, 然后它被緩存后察蹲,此時它豈不是要提示critical系統(tǒng)內(nèi)存请垛??洽议?
這個當(dāng)然不是宗收,系統(tǒng)在啟動時,會啟動很多非persistent的系統(tǒng)應(yīng)用亚兄,如email/calendar/dialer等等混稽,而這些此時用戶并沒有使用過,所以它們大多數(shù)是empty的進(jìn)程审胚,在PIXEL手機測試時匈勋,發(fā)現(xiàn)有10多個empty進(jìn)程,所以系統(tǒng)一開機時膳叨,并不會提示critical系統(tǒng)內(nèi)存洽洁。

內(nèi)存因子
        // We always allow the memory level to go up (better).  We only allow it to go
        // down if we are in a state where that is allowed, *and* the total number of processes
        // has gone down since last time.
        if (memFactor > mLastMemoryLevel) {
            if (!mAllowLowerMemLevel || mLruProcesses.size() >= mLastNumProcesses) {
                memFactor = mLastMemoryLevel;
            }
        }

決定是否對內(nèi)存因子降級

        mLastMemoryLevel = memFactor;
        mLastNumProcesses = mLruProcesses.size();
        boolean allChanged = mProcessStats.setMemFactorLocked(memFactor, !isSleepingLocked(), now);
        final int trackerMemFactor = mProcessStats.getMemFactorLocked();

保存一些變量。

下面的是對內(nèi)存因子為critical, moderate, 以及l(fā)ow的情況下進(jìn)行memory trim.

        if (memFactor != ProcessStats.ADJ_MEM_FACTOR_NORMAL) {
            if (mLowRamStartTime == 0) {
                mLowRamStartTime = now;
            }
            int step = 0;
            int fgTrimLevel;
            switch (memFactor) {
                case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
                    fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL;
                    break;
                case ProcessStats.ADJ_MEM_FACTOR_LOW:
                    fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
                    break;
                default:
                    fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE;
                    break;
            }

將memFactor轉(zhuǎn)換成ComponentCallbacks2中定義的變量名

            int factor = numTrimming/3;
            int minFactor = 2;
            if (mHomeProcess != null) minFactor++;
            if (mPreviousProcess != null) minFactor++;
            if (factor < minFactor) factor = minFactor;
            int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;

計算factor, 這個是步進(jìn)的factor, curLevel,從最高等級開始
numTrimming是指那些比PROCESS_STATE_HOME不重要的進(jìn)程

            for (int i=N-1; i>=0; i--) { //從最近最常使用的進(jìn)程開始菲嘴。
                ProcessRecord app = mLruProcesses.get(i);
                if (allChanged || app.procStateChanged) {
                    setProcessTrackerStateLocked(app, trackerMemFactor, now);
                    app.procStateChanged = false;
                }
                //那些重要性低于ActivityManager.PROCESS_STATE_HOME的進(jìn)程的處理饿自,
               //包括B-Service進(jìn)程、cachedProcess和emptyProcess
                if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME
                        && !app.killedByAm) {
                    if (app.trimMemoryLevel < curLevel && app.thread != null) {
                        try {
                            app.thread.scheduleTrimMemory(curLevel);
                        } catch (RemoteException e) {
                        }
                    }
                    app.trimMemoryLevel = curLevel;
                    step++;
                    //前面的那個步長龄坪,trim等級更高昭雌,每到一個步長,trim等級都下降一個level.
                    if (step >= factor) {
                        step = 0;
                        switch (curLevel) {
                            case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
                                curLevel = ComponentCallbacks2.TRIM_MEMORY_MODERATE;
                                break;
                            case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
                                curLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
                                break;
                        }
                    }
                //heavy 的進(jìn)程
                } else if (app.curProcState == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {
                    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 {
                    //important健田,以及那些短暫的, backup的進(jìn)程 
                    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;
                    }
                    //其它的一些進(jìn)程 
                    if (app.trimMemoryLevel < fgTrimLevel && app.thread != null) {
                            app.thread.scheduleTrimMemory(fgTrimLevel);
                        } catch (RemoteException e) {
                        }
                    }
                    app.trimMemoryLevel = fgTrimLevel;
                }
            }

下面是對系統(tǒng)內(nèi)存為正常的情況下, 對優(yōu)先級低于PROCESS_STATE_IMPORTANT_BACKGROUND烛卧,最多給予TRIM_MEMORY_UI_HIDDEN的提示

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

當(dāng)updateOomAdjLocked在killed超過limit的CACHED/EMPTY進(jìn)程后,接下來會對剩下的CACHED/EMPTY嘗試去做 Memory Trim的動作妓局。也就是觸發(fā)對應(yīng)進(jìn)程的scheduleTrimMemory唱星,試著讓進(jìn)程去釋放一些內(nèi)存雳旅。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市间聊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌抵拘,老刑警劉巖哎榴,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異僵蛛,居然都是意外死亡尚蝌,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門充尉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來飘言,“玉大人,你說我怎么就攤上這事驼侠∽撕瑁” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵倒源,是天一觀的道長苛预。 經(jīng)常有香客問我,道長笋熬,這世上最難降的妖魔是什么热某? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮胳螟,結(jié)果婚禮上昔馋,老公的妹妹穿的比我還像新娘。我一直安慰自己糖耸,他們只是感情好秘遏,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蔬捷,像睡著了一般垄提。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上周拐,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天铡俐,我揣著相機與錄音,去河邊找鬼妥粟。 笑死审丘,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的勾给。 我是一名探鬼主播滩报,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼锅知,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了脓钾?” 一聲冷哼從身側(cè)響起售睹,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎可训,沒想到半個月后昌妹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡握截,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年飞崖,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谨胞。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡固歪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出胯努,到底是詐尸還是另有隱情牢裳,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布康聂,位于F島的核電站贰健,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏恬汁。R本人自食惡果不足惜伶椿,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望氓侧。 院中可真熱鬧脊另,春花似錦、人聲如沸约巷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽独郎。三九已至踩麦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間氓癌,已是汗流浹背谓谦。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留贪婉,地道東北人反粥。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親才顿。 傳聞我的和親對象是個殘疾皇子莫湘,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容