日本搞逼视频_黄色一级片免费在线观看_色99久久_性明星video另类hd_欧美77_综合在线视频

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

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

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

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

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

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

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

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

??內(nèi)核所管理的進(jìn)程都有1個(gè)衡量其oom權(quán)重的值,存儲(chǔ)在/proc/< PID >/oom_adj中。根據(jù)這1權(quán)重值和上面所提及的若干其他因素,系統(tǒng)會(huì)實(shí)時(shí)給每一個(gè)進(jìn)程評(píng)分,以決定OOM時(shí)應(yīng)當(dāng)殺死哪些進(jìn)程。
這個(gè)值存儲(chǔ)在/proc/< PID >/oom_score中。
oom_score分?jǐn)?shù)越低的進(jìn)程,被殺死的幾率越小,或說(shuō)被殺死的時(shí)間越晚。
下面展現(xiàn)了PID為5912的NetworkManager進(jìn)程的oom_adj 和oom_score,可以看到分?jǐn)?shù)很低,說(shuō)明此進(jìn)程10分重要,1般不會(huì)被系統(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)資源”瀕臨絕境”的情況下才會(huì)產(chǎn)生效果,而Android則實(shí)現(xiàn)了自己的Killer.

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

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

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

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

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

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

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

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

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

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

路徑以下:

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

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

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

Android進(jìn)程分類(lèi)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

在AMS 用于處理進(jìn)程的相干代碼文件ProcessList.java 中,定義了不同類(lèi)型的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相比,對(duì)用戶(hù)黏合度小1些 static final int SERVICE_B_ADJ = 8; //用戶(hù)前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í)利用程序進(jìn)程 static final int HEAVY_WEIGHT_APP_ADJ = 4; //用于承載backup相干操作的進(jìn)程 static final int BACKUP_APP_ADJ = 3; //這類(lèi)進(jìn)程能被用戶(hù)感覺(jué)到但不可見(jiàn),如后臺(tái)運(yùn)行的音樂(lè)播放器 static final int PERCEPTIBLE_APP_ADJ = 2; //有前臺(tái)可見(jiàn)的Activity進(jìn)程,如果輕易殺死這類(lèi)進(jìn)程,將嚴(yán)重影響用戶(hù)體驗(yàn) static final int VISIBLE_APP_ADJ = 1; //當(dāng)前正在運(yùn)行的那個(gè)進(jìn)程,即用戶(hù)正在交互的程序 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ù)值來(lái)看,adj越小表示進(jìn)程類(lèi)型就越重要,特別的,系統(tǒng)進(jìn)程的默許oom_adj 為⑴6,這類(lèi)進(jìn)程永久不會(huì)被殺死。

還需要注意的是updateOomLevels函數(shù),內(nèi)部原理是通過(guò)寫(xiě)上面兩個(gè)文件來(lái)實(shí)現(xiàn),AMS 會(huì)根據(jù)系統(tǒng)確當(dā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)的評(píng)定標(biāo)準(zhǔn),我們也能夠用自己的方式來(lái)改變進(jìn)程的權(quán)重值。

1.寫(xiě)文件

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

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,保證它不會(huì)被殺死。

2.設(shè)置android:persistent

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


參考資料

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

生活不易,碼農(nóng)辛苦
如果您覺(jué)得本網(wǎng)站對(duì)您的學(xué)習(xí)有所幫助,可以手機(jī)掃描二維碼進(jìn)行捐贈(zèng)
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關(guān)閉
程序員人生
主站蜘蛛池模板: 天天操天天草 | 美美女高清毛片视频免费观看 | 能免费看av的网站 | 亚洲一区二区三区四区在线 | 最近的中文字幕 | 欧美日韩精品一区 | av久久久 | 亚洲区一区二区三区 | 日本a在线| 午夜精品久久久久久久白皮肤 | 国产专区视频 | 综合激情久久 | 久久尤物 | 中文在线一区 | 性天堂 | 久久九九九九 | 国产精品国产三级国产三级人妇 | 国产精品区一区二区三区 | 亚洲国产高清视频 | 久艹av | 国产专区一区二区 | 亚洲精品久久久久久久久久久久久久 | 成人欧美一区二区三区黑人孕妇 | 三级av毛片| 精品中文字幕一区 | 成年网站在线观看 | 国产一卡二卡三卡 | 国产成人精品一区二 | 三级毛片视频网站 | 久久久毛片 | 久草综合在线 | 久久久久久免费电影 | 亚洲人成人一区二区在线观看 | 欧美电影一区二区 | 日日夜夜超碰 | 久久成人一区 | 国产美女无遮挡jk免费视频软件 | 日韩av成人网 | 狠狠艹 | 国产亚洲欧美另类一区二区三区 | 欧美综合自拍 |