多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內(nèi)最全I(xiàn)T社區(qū)平臺 聯(lián)系我們 | 收藏本站
阿里云優(yōu)惠2
您當(dāng)前位置:首頁 > php開源 > 綜合技術(shù) > Android 內(nèi)存管理機(jī)制詳解

Android 內(nèi)存管理機(jī)制詳解

來源:程序員人生   發(fā)布時間:2016-12-06 10:38:45 閱讀次數(shù):5606次

??嵌入式裝備的1個普遍特點(diǎn)是內(nèi)存容量相對有限。當(dāng)運(yùn)行的程序超過1定數(shù)量時,或觸及復(fù)雜的計(jì)算時,極可能出現(xiàn)內(nèi)存不足,進(jìn)而致使系統(tǒng)卡頓的現(xiàn)象。Android 系統(tǒng)也不例外,它一樣面臨著裝備物理內(nèi)存短缺的窘境。對已啟動過1次的Android程序,再1次啟動所花的時間會明顯減少。緣由在于Android系統(tǒng)其實(shí)不馬上清算那些已”淡出視野”的程序(比如你調(diào)用Activity.finish退出UI界面)。它們在1定的時間里依然駐留在內(nèi)存中。這樣做的好處是明顯的,即下1次啟動不需要再為程序重新創(chuàng)建1個進(jìn)程;壞處就是,加大了內(nèi)存OOM的幾率。

Linux內(nèi)存監(jiān)控機(jī)制(OOMKiller)

??Android是基于Linux的,而Linux底層內(nèi)核有自己的內(nèi)存監(jiān)控機(jī)制,即OOMKiller。1旦發(fā)現(xiàn)系統(tǒng)的可用內(nèi)存到達(dá)臨界值,這個OOM的管理者就會自動跳出來清算內(nèi)存。

OOMKiller有不同的策略和不同的處理手段。它的核心思想以下:

依照優(yōu)先級順序,從低到高逐漸殺掉進(jìn)程,回收內(nèi)存。

優(yōu)先級的設(shè)定策略主要斟酌兩個方面:1個是要斟酌對系統(tǒng)的侵害程度(例如系統(tǒng)的核心進(jìn)程,優(yōu)先級通常較高),另外一方面也希望盡量多地釋放無用內(nèi)存。1個公道的策略最少需要斟酌以下幾個因素:

  • 進(jìn)程消耗的內(nèi)存
  • 進(jìn)程占用的CPU時間
  • oom_adj(OOM權(quán)重)

??內(nèi)核所管理的進(jìn)程都有1個衡量其oom權(quán)重的值,存儲在/proc/< PID >/oom_adj中。根據(jù)這1權(quán)重值和上面所提及的若干其他因素,系統(tǒng)會實(shí)時給每一個進(jìn)程評分,以決定OOM時應(yīng)當(dāng)殺死哪些進(jìn)程。
這個值存儲在/proc/< PID >/oom_score中。
oom_score分?jǐn)?shù)越低的進(jìn)程,被殺死的幾率越小,或說被殺死的時間越晚。
下面展現(xiàn)了PID為5912的NetworkManager進(jìn)程的oom_adj 和oom_score,可以看到分?jǐn)?shù)很低,說明此進(jìn)程10分重要,1般不會被系統(tǒng)殺死。

oom_score

Android 內(nèi)存管理機(jī)制

基于Linux內(nèi)核OOM Killer的核心思想,Android 系統(tǒng)擴(kuò)大出了自己的內(nèi)存監(jiān)控體系。由于Linux下的內(nèi)存殺手需要等到系統(tǒng)資源”瀕臨絕境”的情況下才會產(chǎn)生效果,而Android則實(shí)現(xiàn)了自己的Killer.

Android 系統(tǒng)為此開發(fā)了1個專門的驅(qū)動,名為Low Memory Killer(LMK)。源碼路徑在內(nèi)核工程的 drivers/staging/android/Lowmemorykiller.c中。
它的驅(qū)動加載函數(shù)以下:

static int __init lowmem_init(void) { register_shrinker(&lowmem_shrinker); return 0; }

可見LMK向內(nèi)核線程注冊了1個shrinker的監(jiān)聽回調(diào),實(shí)現(xiàn)體為lowmem_shrinker。當(dāng)系統(tǒng)的空閑頁面低于1定閾值時,這個回調(diào)就會被履行。

Lowmemorykiller.c 中定義了兩個數(shù)組,分別以下:

static short lowmem_adj[6] = { 0, 1, 6, 12, }; static int lowmem_adj_size = 4;//下面的數(shù)值以此為單位(頁大小) static int lowmem_minfree[6] = { 3 * 512, /* 6MB */ 2 * 1024, /* 8MB */ 4 * 1024, /* 16MB */ 16 * 1024, /* 64MB */ };

第1個數(shù)組lowmem_adj最多有6個元素,默許只定義了4個,它表示可用容量處于”某層級”時需要被處理的adj值;第2個數(shù)組則是對”層級”的描寫。這樣說可能不清楚,舉個例子,lowmem_minfree 的第1個元素是3*512,3*512*lowmem_adj_size=6MB.意味著當(dāng)可用內(nèi)存小于6MB時,Killer需要清算adj的值為0(即lowmem_adj的第1個元素)以下的那些進(jìn)程。其中adj的取值范圍是⑴7~15,數(shù)字越小表示進(jìn)程級別越高,通常只有0⑴5被使用。

下圖是LWK機(jī)制的實(shí)現(xiàn)簡圖。

LWK實(shí)現(xiàn)簡圖

這兩個數(shù)組只是系統(tǒng)的預(yù)定義值,我們可以根據(jù)項(xiàng)目的實(shí)際需求來定制。

Android系統(tǒng)提供了相應(yīng)的文件來供我們修改這兩組值。

路徑以下:

/sys/module/lowmemorykiller/parameters/adj
/sys/module/lowmemorykiller/parameters/minfree

可以在 init.rc(系統(tǒng)啟動時,由init進(jìn)程解析的第1個腳本)中加入以下語句。init.rc路徑為/system/core/rootdir/init.rc

/sys/module/lowmemorykiller/parameters/adj  0,8
/sys/module/lowmemorykiller/parameters/minfree 1024,4096

Android進(jìn)程分類

進(jìn)程omm_adj的大小跟進(jìn)程的類型和進(jìn)程被調(diào)度的次序有關(guān)。

在Android中,進(jìn)程主要分為以下幾個種類:

1. 前臺進(jìn)程(foreground)

??目前正在屏幕上顯示的進(jìn)程和1些系統(tǒng)進(jìn)程。舉例來講,Dialer,Storage,Google Search等系統(tǒng)進(jìn)程就是前臺進(jìn)程;再舉例來講,當(dāng)你運(yùn)行1個程序,如閱讀器,當(dāng)閱讀器界面在前臺顯示時,閱讀器屬于前臺進(jìn)程(foreground),但1旦你按home回到主界面,閱讀器就變成了后臺程序(background)。我們最不希望終止的進(jìn)程就是前臺進(jìn)程。

2. 可見進(jìn)程(visible)

??可見進(jìn)程是1些不再前臺,但用戶仍然可見的進(jìn)程,舉個例來講:widget、輸入法等,都屬于visible。這部份進(jìn)程雖然不在前臺,但與我們的使用也密切相干,我們也不希望它們被終止(你肯定不希望時鐘、天氣,新聞等widget被終止,那它們將沒法同步,你也不希望輸入法被終止,否則你每次輸入時都需要重新啟動輸入法)。

3. 桌面進(jìn)程(home app)

??即launcher,保證在多任務(wù)切換以后,可以快速返回到home界面而不需重新加載launcher。

4. 次要服務(wù)(secondary server)

??目前正在運(yùn)行的1些服務(wù)(主要服務(wù),如撥號等,是不可能被進(jìn)程管理終止的,故這里只談次要服務(wù)),舉例來講:谷歌企業(yè)套件,Gmail內(nèi)部存儲,聯(lián)系人內(nèi)部存儲等。這部份服務(wù)雖然屬于次要服務(wù),但很1些系統(tǒng)功能仍然息息相干,我們經(jīng)常需要用到它們,所以也不太希望他們被終止。

5. 后臺進(jìn)程(hidden)

??即是后臺進(jìn)程(background),就是我們通常意義上理解的啟動后被切換到后臺的進(jìn)程,如閱讀器,瀏覽器等。當(dāng)程序顯示在屏幕上時,他所運(yùn)行的進(jìn)程即為前臺進(jìn)程(foreground),1旦我們按home返回主界面(注意是按home,不是按back),程序就駐留在后臺,成為后臺進(jìn)程(background)。后臺進(jìn)程的管理策略有多種:有較為積極的方式,1旦程序到達(dá)后臺立即終止,這類方式會提高程序的運(yùn)行速度,但沒法加速程序的再次啟動;也有較消極的方式,盡量多的保存后臺程序,雖然可能會影響到單個程序的運(yùn)行速度,但在再次啟動已啟動的程序時,速度會有所提升。這里就需要用戶根據(jù)自己的使用習(xí)慣找到1個平衡點(diǎn)。

6. 內(nèi)容供應(yīng)節(jié)點(diǎn)(content provider)

沒有程序?qū)嶓w,進(jìn)提供內(nèi)容供別的程序去用的,比如日歷供應(yīng)節(jié)點(diǎn),郵件供應(yīng)節(jié)點(diǎn)等。在終止進(jìn)程時,這類程序應(yīng)當(dāng)有較高的優(yōu)先權(quán)。

7. 空進(jìn)程(empty)

??沒有任何東西在內(nèi)運(yùn)行的進(jìn)程,有些程序,比如BTE,在程序退出后,仍然會在進(jìn)程中駐留1個空進(jìn)程,這個進(jìn)程里沒有任何數(shù)據(jù)在運(yùn)行,作用常常是提高該程序下次的啟動速度或記錄程序的1些歷史信息。這部份進(jìn)程無疑是應(yīng)當(dāng)最早終止的

在AMS 用于處理進(jìn)程的相干代碼文件ProcessList.java 中,定義了不同類型的adj值,以下所示:

/** *省略其它代碼 */ //未知的adj static final int UNKNOWN_ADJ = 16; static final int CACHED_APP_MAX_ADJ = 15; static final int CACHED_APP_MIN_ADJ = 9; //B list of service ,和A list相比,對用戶黏合度小1些 static final int SERVICE_B_ADJ = 8; //用戶前1次交互的進(jìn)程 static final int PREVIOUS_APP_ADJ = 7; //Launcher進(jìn)程 static final int HOME_APP_ADJ = 6; //運(yùn)行了application service的進(jìn)程 static final int SERVICE_ADJ = 5; //重量級利用程序進(jìn)程 static final int HEAVY_WEIGHT_APP_ADJ = 4; //用于承載backup相干操作的進(jìn)程 static final int BACKUP_APP_ADJ = 3; //這類進(jìn)程能被用戶感覺到但不可見,如后臺運(yùn)行的音樂播放器 static final int PERCEPTIBLE_APP_ADJ = 2; //有前臺可見的Activity進(jìn)程,如果輕易殺死這類進(jìn)程,將嚴(yán)重影響用戶體驗(yàn) static final int VISIBLE_APP_ADJ = 1; //當(dāng)前正在運(yùn)行的那個進(jìn)程,即用戶正在交互的程序 static final int FOREGROUND_APP_ADJ = 0; static final int PERSISTENT_SERVICE_ADJ = -11; //persistent性質(zhì)的進(jìn)程,如telephony static final int PERSISTENT_PROC_ADJ = -12; //系統(tǒng)進(jìn)程 static final int SYSTEM_ADJ = -16; /** *省略其它代碼 */

上面定義的adj數(shù)值來看,adj越小表示進(jìn)程類型就越重要,特別的,系統(tǒng)進(jìn)程的默許oom_adj 為⑴6,這類進(jìn)程永久不會被殺死。

還需要注意的是updateOomLevels函數(shù),內(nèi)部原理是通過寫上面兩個文件來實(shí)現(xiàn),AMS 會根據(jù)系統(tǒng)確當(dāng)前配置自動修正adj和minfree,以盡量適配不同的硬件。函數(shù)源碼以下所示:

private void updateOomLevels(int displayWidth, int displayHeight, boolean write) { // Scale buckets from avail memory: at 300MB we use the lowest values to // 700MB or more for the top values. float scaleMem = ((float)(mTotalMemMb-300))/(700-300); // Scale buckets from screen size. int minSize = 480*800; // 384000 int maxSize = 1280*800; // 1024000 230400 870400 .264 float scaleDisp = ((float)(displayWidth*displayHeight)-minSize)/(maxSize-minSize); if (false) { Slog.i("XXXXXX", "scaleMem=" + scaleMem); Slog.i("XXXXXX", "scaleDisp=" + scaleDisp + " dw=" + displayWidth + " dh=" + displayHeight); } float scale = scaleMem > scaleDisp ? scaleMem : scaleDisp; if (scale < 0) scale = 0; else if (scale > 1) scale = 1; int minfree_adj = Resources.getSystem().getInteger( com.android.internal.R.integer.config_lowMemoryKillerMinFreeKbytesAdjust); int minfree_abs = Resources.getSystem().getInteger( com.android.internal.R.integer.config_lowMemoryKillerMinFreeKbytesAbsolute); if (false) { Slog.i("XXXXXX", "minfree_adj=" + minfree_adj + " minfree_abs=" + minfree_abs); } // We've now baked in the increase to the basic oom values above, since // they seem to be useful more generally for devices that are tight on // memory than just for 64 bit. This should probably have some more // tuning done, so not deleting it quite yet... final boolean is64bit = false; //Build.SUPPORTED_64_BIT_ABIS.length > 0; for (int i=0; i<mOomAdj.length; i++) { int low = mOomMinFreeLow[i]; int high = mOomMinFreeHigh[i]; mOomMinFree[i] = (int)(low + ((high-low)*scale)); if (is64bit) { // On 64 bit devices, we consume more baseline RAM, because 64 bit is cool! // To avoid being all pagey and stuff, scale up the memory levels to // give us some breathing room. mOomMinFree[i] = (3*mOomMinFree[i])/2; } } if (minfree_abs >= 0) { for (int i=0; i<mOomAdj.length; i++) { mOomMinFree[i] = (int)((float)minfree_abs * mOomMinFree[i] / mOomMinFree[mOomAdj.length - 1]); } } if (minfree_adj != 0) { for (int i=0; i<mOomAdj.length; i++) { mOomMinFree[i] += (int)((float)minfree_adj * mOomMinFree[i] / mOomMinFree[mOomAdj.length - 1]); if (mOomMinFree[i] < 0) { mOomMinFree[i] = 0; } } } // The maximum size we will restore a process from cached to background, when under // memory duress, is 1/3 the size we have reserved for kernel caches and other overhead // before killing background processes. mCachedRestoreLevel = (getMemLevel(ProcessList.CACHED_APP_MAX_ADJ)/1024) / 3; // Ask the kernel to try to keep enough memory free to allocate 3 full // screen 32bpp buffers without entering direct reclaim. int reserve = displayWidth * displayHeight * 4 * 3 / 1024; int reserve_adj = Resources.getSystem().getInteger(com.android.internal.R.integer.config_extraFreeKbytesAdjust); int reserve_abs = Resources.getSystem().getInteger(com.android.internal.R.integer.config_extraFreeKbytesAbsolute); if (reserve_abs >= 0) { reserve = reserve_abs; } if (reserve_adj != 0) { reserve += reserve_adj; if (reserve < 0) { reserve = 0; } } if (write) { ByteBuffer buf = ByteBuffer.allocate(4 * (2*mOomAdj.length + 1)); buf.putInt(LMK_TARGET); for (int i=0; i<mOomAdj.length; i++) { buf.putInt((mOomMinFree[i]*1024)/PAGE_SIZE); buf.putInt(mOomAdj[i]); } writeLmkd(buf); SystemProperties.set("sys.sysctl.extra_free_kbytes", Integer.toString(reserve)); } // GB: 2048,3072,4096,6144,7168,8192 // HC: 8192,10240,12288,14336,16384,20480 }

改變進(jìn)程權(quán)重adj值

除系統(tǒng)的評定標(biāo)準(zhǔn),我們也能夠用自己的方式來改變進(jìn)程的權(quán)重值。

1.寫文件

??和上述提到的adj和minfree類似,進(jìn)程的oom_adj也能夠通過寫文件的情勢來修改,路徑為/proc/< PID >/oom_adj。比如init.rc中有以下語句

on early-init
 # Set init and its forked children's oom_adj.
 write /proc/1/oom_adj ⑴6

PID 為1的進(jìn)程為init程序,將此進(jìn)程的oom_adj 設(shè)置為⑴6,保證它不會被殺死。

2.設(shè)置android:persistent

??對某些非常重要的程序,不希望它被系統(tǒng)殺死。在AndroidMainifest.xml文件中給application 標(biāo)簽添加”android:persistent=true”屬性,將利用程序設(shè)置為常駐內(nèi)存,但需要特別注意,如果利用程序本不夠完善,而系統(tǒng)又不能正常回收,那末會致使意想不到的問題。


參考資料

《深入理解Android內(nèi)核設(shè)計(jì)思想》

生活不易,碼農(nóng)辛苦
如果您覺得本網(wǎng)站對您的學(xué)習(xí)有所幫助,可以手機(jī)掃描二維碼進(jìn)行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關(guān)閉
程序員人生
主站蜘蛛池模板: 自怕偷自怕亚洲精品 | 欧美特级一级毛片 | 自拍欧美| 一区二区三区在线 | 网站 | 欧美高清videos36opsexhd | 国产一区亚洲二区三区 | 波多野结衣精品一区二区三区 | 自拍偷拍视频网站 | 国产精品国产三级国产专区不 | 国精品日韩欧美一区二区三区 | babes性欧美高清 | 国产精品久久久久国产精品三级 | 亚洲另类春色小说 | 国产成人系列 | 中文字幕一二三区乱码 | 日本亚州在线播放精品 | 91真人毛片一级在线播放 | 最近的中文字幕免费视频1 最近的中文字幕免费完整 最近的中文字幕视频大全高清 | 秋霞中文字幕 | 亚洲精品456 | 免费福利影院 | 国产精品视频免费一区二区三区 | 欧美成人精品一区二三区在线观看 | 日韩专区欧美 | 亚洲欧美久久 | 可以免费观看一级毛片黄a 可以免费看的黄色网址 | 手机看片日韩在线 | jizz视频在线观看 | 久久视频精品a线视频在线观看 | 欧美一级毛片高清免费观看 | 一级aa免费视频毛片 | 国产精品久久久久久久久久直 | 久久伊人婷婷 | 狠狠涩 | 男人边吃奶边做好爽男女视频 | 午夜久久久久久网站 | 另类图片另类小说 | 成人在线手机视频 | 免费网站在线观看国产v片 免费网站在线看 | 精品伊人久久久大香线蕉欧美 | 日韩精品1区 |