前傳
嵌入式系統(tǒng)的內(nèi)存回收還是比較重要的,因為這塊涉及到程序運行性能。
嵌入式系統(tǒng)(比如平板,手機)會更加關(guān)注單機性能優(yōu)化,因而會更加重視系統(tǒng)內(nèi)存回收。
嵌入式系統(tǒng)不像互聯(lián)網(wǎng)那種大型分布式服務(wù)器系統(tǒng),他們往往內(nèi)存和存儲容量比較充裕,因而關(guān)注點在分布式方面,對單機性能不夠重視。嵌入式系統(tǒng),在有限的內(nèi)存和存儲空間因素制約下,會更加關(guān)注單機性能優(yōu)化。
而內(nèi)存回收這塊是比較重要的,因為內(nèi)存回收做的不好,內(nèi)存壓力得不到釋放,最直接的是內(nèi)存壓力會轉(zhuǎn)化為IO壓力,對系統(tǒng)io性能造成影響。另外也會轉(zhuǎn)換為cpu壓力,影響程序的cpu資源使用。
所以結(jié)合我對內(nèi)存回收方面的調(diào)研,想重點寫下對Linux內(nèi)核內(nèi)存回收這塊代碼的理解,也想分享下我在這塊的調(diào)研心得。
內(nèi)存回收的重要性
這個內(nèi)存回收方面的優(yōu)化對系統(tǒng)到底有怎樣的影響,我想舉幾個例子會詳細透徹地講下內(nèi)存回收方面的優(yōu)化,還有給系統(tǒng)帶來的好處。
一 、內(nèi)存回收在高負載系統(tǒng)性能方面的優(yōu)化
1 問題現(xiàn)象:
移動設(shè)備上(比如平板,手機)后臺u盤傳輸大批量數(shù)據(jù)時,前臺桌面操作卡頓。
2 原因分析:
1: u盤拷貝數(shù)據(jù)量大時,會導(dǎo)致系統(tǒng)內(nèi)存里面臟頁變多,然后可用內(nèi)存變少。
2: 然后前臺平板操作時,會allocate_pages去分配內(nèi)存頁。這個時候會陷入slow path,去觸發(fā)內(nèi)存回收。
由于此時有大量臟頁存在,內(nèi)存回收會比較耗時,這樣前臺操作的內(nèi)存分配性能也會變差。同時回收臟頁時產(chǎn)生的io壓力對前臺操作時的io性能也會有影響。
因為前臺的內(nèi)存和io性能都受到了影響,所以前臺平板操作才會變卡頓。
3 一點性能優(yōu)化心得方面的精彩點評:
這是一個典型的單機系統(tǒng)里面比較特色的內(nèi)存和io相互作用導(dǎo)致的性能問題。
1) 內(nèi)存回收時,不得不做很多臟頁回寫工作
因為單機系統(tǒng)比如平板,手機,里面大都是buffer寫,寫到page cache里面就認為工作完成就成功返回了, 雖然省事,但是寫到page cache里面必然會帶來額外的內(nèi)存消耗。
所以我們看到嵌入式系統(tǒng)里面很多場景下(比如上面的u盤拷貝場景),會造成某些時間段內(nèi)系統(tǒng)page cache占內(nèi)存多,其實是里面積累很多臟頁。
pagecache占內(nèi)存多,系統(tǒng)free內(nèi)存就會變少。而偏偏系統(tǒng)的write back線程又不活躍(手機ext4文件系統(tǒng)里面開啟延遲寫后,系統(tǒng)每隔30s才回寫一次臟頁),這樣就會造成 [內(nèi)存分配 → 不得不做內(nèi)存回收] 這樣路徑下,才會解決pagecache占內(nèi)存多問題,具體就是觸發(fā)做臟頁回寫工作。
2) 問題就在于:
系統(tǒng)的io寫性能不好時,會影響到page cache里面的臟頁釋放。臟頁一旦得不到有效釋放,系統(tǒng)free內(nèi)存越來越少,這樣內(nèi)存分配性能也會受到影響。
同時內(nèi)存分配有問題或者回收效率不高,在處理臟頁回寫方面有缺陷時,也會產(chǎn)生額外的io壓力。所以是內(nèi)存和io相互影響,相互作用。
所以就有了內(nèi)核社區(qū)每年那個storage and mm國際會議,單機系統(tǒng)里面出現(xiàn)性能問題時,內(nèi)存和io往往需要同時去優(yōu)化。
3) 先去優(yōu)化內(nèi)存,如果不行,再去優(yōu)化io
系統(tǒng)的內(nèi)存和io相互作用導(dǎo)致的性能問題出現(xiàn)時,最好用這種優(yōu)化思路,原因很簡單,內(nèi)存優(yōu)化成本低,風(fēng)險小。
為什么國內(nèi)有很多技術(shù)文章或者書籍都詳細重點講linux內(nèi)核內(nèi)存管理,為什么文件系統(tǒng)和io這塊不去詳細講講,其中有一個重要的原因是:
因為國內(nèi)企業(yè)普遍看到了內(nèi)存優(yōu)化成本低,不行就多殺一些進程或者優(yōu)化下內(nèi)存分配和回收,即使是內(nèi)存出問題,頂多重啟一下系統(tǒng),問題就沒了。
但是io這塊本身技術(shù)方面就比較復(fù)雜,光內(nèi)核里面,從vfs到ext4具體文件系統(tǒng),再到塊設(shè)備層,再到存儲bsp層,再到存儲硬件芯片層,這些每個層優(yōu)化的不好,都會出io性能問題,而且部署io性能優(yōu)化方案,往往又要對這些每個層都要有所熟悉,這樣才能保證優(yōu)化副作用降到最小。
這還是討論優(yōu)化技術(shù)本身的,還沒談到,如果存儲io出問題,手機上重要文件損壞,輕則用戶發(fā)現(xiàn)文件丟失(這樣的損壞和丟失是你再開機多少次都無法挽回的),重則無法開機事故就出來了。
詳細可以見我的另外一篇技術(shù)文檔:《手機Android存儲性能優(yōu)化架構(gòu)分析》。
4 解決思路:
在社區(qū)高版本內(nèi)核上面找了一些patch,這些patch是對內(nèi)核內(nèi)存回收方面的優(yōu)化,重點也是優(yōu)化臟頁過多時,內(nèi)存回收耗時問題的。
優(yōu)化1:
1)優(yōu)化原理
內(nèi)存回收時,提前喚醒write-back內(nèi)核線程,提前把所有的臟頁都寫入到磁盤上。這樣交給專門的內(nèi)核回寫線程來做臟頁回寫工作,這樣效率更高。
因為內(nèi)存回收路徑中做的臟頁回收是離散寫,具體是調(diào)用pageout → 文件系統(tǒng)writepage函數(shù),這樣每回只寫一個page,在lru鏈表上積累的臟頁比較多時,這樣回寫效率并不高,因此也影響了內(nèi)存回收。
提前喚醒writeback線程后,writeback線程寫會集中寫,具體是以inode為單元,會把每個inode上的臟頁數(shù)據(jù)通過ext4的writepages函數(shù),寫到磁盤上。
如果寫的臟頁數(shù)據(jù)量都一樣,集中寫會做io請求合并,減少io請求處理耗時,這樣顯然比內(nèi)存回收自己離散寫臟頁性能要好多了。
具體性能優(yōu)化數(shù)據(jù)可見我的另外一篇文檔:那些年解的疑難性能問題 - ext4碎片整理。
2)patch內(nèi)容
具體是社區(qū)這個patch: mm/vmscan: wake up flushers for legacy cgroups too.
在內(nèi)存回收必經(jīng)路徑shrink_inactive_list函數(shù)里面,判斷stat.nr_unqueued_dirty == nr_taken的話,說明此時內(nèi)核的inactive lru list里面積累了大量臟頁,需要喚醒writeback線程去集中大批量地寫一次。
優(yōu)化2:
1) 優(yōu)化原理
在內(nèi)存回收路徑里面盡量少回收臟頁,少觸發(fā)io操作,這樣會降低內(nèi)存回收direct reclaim路徑的耗時,也會間接優(yōu)化內(nèi)存分配slow_path的耗時。
同時為了保證多回收內(nèi)存,增加更多free page, 會多回收些干凈頁。核心工作是:
把active list上更多的page(比如clean page)加入到inactive list里面,這樣雖然會造成比如這些active clean page被回收后,很有可能還要被重新讀入內(nèi)存。但是這個負作用比起 陷入緩慢的write back臟頁操作不能立刻滿足前臺內(nèi)存分配需求 要輕得多。
因為存儲芯片讀是比寫要快很多的,所以上面雖然有那個負作用,但是不足為慮。
2) patch內(nèi)容
具體是社區(qū)這2個patch:
mm: vmscan: only write dirty pages that the scanner has seen twice.
在shrink_inactive_list函數(shù)里面,如果direct reclaim,則做以下工作:
是待回收頁是臟頁時,進一步判斷如果該頁沒有設(shè)置reclaim標(biāo)記,那么就僅僅設(shè)置下relcaim標(biāo)記,重新放回active list上,而不去回收它。然后等到第2次再碰到該頁時,如果還是臟頁,再去回收它。
mm: vmscan: move dirty pages out of the way until they're flushed
在lru_add_drain里面的pagevec_move_tail_fn函數(shù)里面修改,搞成不管該page是否active,都盡可能地把它放到inactive list上去。
這樣會盡量把active list上的page往inactive list上轉(zhuǎn)移,因為上面patch是在inactive list上碰到臟頁放到active list上的,所以再帶上這個patch,那么最終效果是更多的clean page被搬到了inactive list上去,這樣就會有更多的page被回收掉。
這樣就會充分釋放了內(nèi)存,接下來的內(nèi)存分配性能就會得到優(yōu)化。
二、 內(nèi)存回收里面的boost watermark優(yōu)化改造
改造1
1 問題現(xiàn)象
嵌入式設(shè)備里面有時候會出現(xiàn)app熱啟動慢,抓trace分析后,是啟動時的io讀性能差,差的原因是很多讀不是從pagecache中讀,而是直接從磁盤上讀。
2 原因分析
有些嵌入式設(shè)備內(nèi)存并不充裕,然后從log中看到,出問題的內(nèi)核里面都開啟了boost watermark。這個特性一旦被開啟,就會使得內(nèi)核內(nèi)存回收變得更加活躍,并且是只回收干凈文件頁的,臟頁和匿名頁都不回收。
這個特性推出的目的本來是為了降低系統(tǒng)內(nèi)存碎片的(詳見我另外一篇文檔:android內(nèi)存碎片優(yōu)化梳理),但是結(jié)果在低內(nèi)存設(shè)備上副作用更加明顯,更加大于它的收益,把app啟動時io讀成功的文件頁都給回收了,這樣就會造成系統(tǒng)的整體io讀性能變差。
3 優(yōu)化思路
低內(nèi)存嵌入式設(shè)備上因為io讀性能差問題嚴重,所以可以關(guān)閉該boost watermark優(yōu)化,高內(nèi)存設(shè)備上保留。
ps:
性能優(yōu)化有時就是這樣一種折衷,優(yōu)化有時候會難免有一些副作用,怎么針對具體問題場景,做到衡量評估好收益和副作用的平衡和折衷,是關(guān)鍵的。
改造2
1 問題現(xiàn)象
移動設(shè)備上(比如手機,平板)相機場景里整機內(nèi)存壓力大,會造成相機相關(guān)進程內(nèi)存分配性能差,出現(xiàn)相機操作卡頓問題。
2 原因分析
相機某些操作場景下,相機自身進程會不可避免有高峰值內(nèi)存分配的需求。當(dāng)陡然切換到這些操作場景時,由于系統(tǒng)沒有做好應(yīng)對準(zhǔn)備,滿足不了相機內(nèi)存分配需求,就會造成相機內(nèi)存性能變差。
3 優(yōu)化思路
相機場景下可以部署下主動內(nèi)存回收方案,在高峰值內(nèi)存場景下,可以緩解相機內(nèi)存分配壓力。
主動內(nèi)存回收必須快捷高效,能短時間內(nèi)釋放出大量可用內(nèi)存出來。這樣可以借鑒下boost watermark的思想,先回收下文件頁,因為文件頁比匿名頁回收要省時多了。
另外一點是相機自身進程的io讀需求比較少,io讀壓力不大,壓力大的是內(nèi)存和cpu,所以為了短時間內(nèi)釋放內(nèi)存壓力,是可以多回收些文件頁的。
同時改造下boost watermark,把它綁到小核上去干活,避免和相機進程爭用cpu。
三、 從系統(tǒng)全局考慮部署高效內(nèi)存回收方案
1 問題背景
嵌入式系統(tǒng),諸如手機,平板,前面提過,內(nèi)存和存儲容量受限,但用戶對它的使用需求卻在日益膨脹,幾乎當(dāng)成電腦一樣在用。
所以單機系統(tǒng)里面,整機內(nèi)存壓力是比較大的,而內(nèi)存方面的優(yōu)化涉及面廣,拿android系統(tǒng)來說,從app到fwk, 再到native層的glibc庫,再到底層內(nèi)核,都會有內(nèi)存優(yōu)化空間。
內(nèi)核內(nèi)存優(yōu)化比較麻煩耗時些,而且有些內(nèi)存性能問題,從上層入手優(yōu)化,反而更加高效快捷些,所以需要從系統(tǒng)全局考慮出發(fā),去優(yōu)化整機內(nèi)存。
2 原生android已有的內(nèi)存回收方案
1)用戶態(tài)的lmkd + 內(nèi)核的內(nèi)存psi兩者結(jié)合,高效殺進程
殺進程是釋放整機內(nèi)存壓力最好的方式,系統(tǒng)整機free內(nèi)存很少時,通過殺掉一些手機后臺低優(yōu)先級進程,可以快速地騰出可用內(nèi)存,供前臺app使用。
另外基于內(nèi)存psi感知,這樣可以更靈敏地感知到程序有性能問題時,就去及時殺進程。
2)Lmkd殺進程缺陷
目前的缺陷是,光根據(jù)adj來進行殺進程優(yōu)先級排序還不夠,有些用戶經(jīng)常使用到的app還是會被頻繁殺掉,這樣會影響到用戶體驗。
另外不區(qū)分主進程和子進程,殺掉主進程就會影響到后臺app駐留。
所以還需要在fwk層做些優(yōu)化工作,因為fwk層最能感知到app業(yè)務(wù)層的變化,在這里最能根據(jù)用戶體驗來部署優(yōu)化方案。
3)其他的一些優(yōu)化方法
從性能優(yōu)化工作角度出發(fā),感覺到目前的linux內(nèi)核內(nèi)存管理這塊,更傾向于服務(wù)互聯(lián)網(wǎng)業(yè)務(wù)場景。嵌入式單機設(shè)備場景比如android手機,想往內(nèi)核主線分支進性能優(yōu)化changes,會發(fā)現(xiàn)比較困難。
所以現(xiàn)在我們看到的比較新的版本上內(nèi)核內(nèi)存回收這塊還是有很多性能問題,有很多待優(yōu)化空間的。
所以一些諸如高通芯片原廠,在linux內(nèi)核主線版本上,會打上一些關(guān)于嵌入式系統(tǒng)方面的優(yōu)化(比如process reclaim等),才會交給我們使用。
Linux內(nèi)核內(nèi)存回收的一些問題和待優(yōu)化空間
一、 內(nèi)存回收目標(biāo)和收益方面的不確定性
1 內(nèi)存回收目標(biāo)
問題1)
direct reclaim時,nr_to_reclaim是它的回收目標(biāo),但這個現(xiàn)在固定死了是32個page(詳見__alloc_pages_direct_reclaim → try_to_free_pages里面的 nr_to_reclaim被強制賦值為SWAP_CLUSTER_MAX),
那么如果內(nèi)存分配只需要1 - 4個page時,陷入到slow path里面做內(nèi)存回收,客戶只需要回收1 - 4個page就行了,但是內(nèi)存回收這塊會多回收出額外的31個page出來。額外的回收工作必然會導(dǎo)致回收要多耗時點。
打開底層ftrace,會經(jīng)??吹角芭_操作app時,對應(yīng)的缺頁異常里面會每次只分配1到4個page,說明在android系統(tǒng)里面分配少量page的需求還是很多的。
原因
這個地方定成32,可能是考慮到系統(tǒng)中有很多進程在做并發(fā)direct reclaim的,所以為了權(quán)衡系統(tǒng)整體reclaim的壓力和避免更多有用內(nèi)存頁被回收掉,這個地方就定成了32.
問題2)
內(nèi)核內(nèi)存回收direct reclaim的必經(jīng)函數(shù)shrink_node_memcg最下面,完成回收目標(biāo)后,還要做rebalance the anon lru active/inactive ratio工作,勢必會增加direct reclaim進程的耗時。
另外shrink_inactive_list里面還要做too_many_isolated工作,這個會導(dǎo)致direct reclaim進程睡眠。
原因
可能是為了服務(wù)器場景考慮的,為了優(yōu)化系統(tǒng)全局內(nèi)存狀態(tài),做一些balance工作。但是嵌入式場景下,該內(nèi)存回收架構(gòu)并未區(qū)分前臺后,這樣前后臺進程在direct reclaim方面一視同仁,
都要做很多balance系統(tǒng)的工作,這樣的話,會導(dǎo)致前臺app的內(nèi)存分配耗時增加。
總結(jié):
上面一些問題的發(fā)現(xiàn),說明內(nèi)存回收這塊目前的架構(gòu)設(shè)計是為服務(wù)器場景考慮的,而嵌入式場景,比如手機,比較關(guān)注前臺app進程的響應(yīng)性能的情況下(詳見我的文檔:手機前后臺io分組優(yōu)化調(diào)研),
上面內(nèi)存回收的一些耗時不確定性問題,至少說明在內(nèi)存回收這個地方,嵌入式設(shè)備尤其是相機場景大內(nèi)存分配的情況下,還是會有優(yōu)化空間的。
2 內(nèi)存回收收益計算
問題
內(nèi)核在做內(nèi)存回收過程中,要調(diào)用shrink_slab去回收系統(tǒng)的一些緩存。但是內(nèi)核drivers/staging目錄下面一些緩存,比如ion緩存,就沒有計算這部分緩存實際被回收的page數(shù)量。
這樣會造成direct reclaim中,比如回收目標(biāo)是32個page,本來帶上ion cached部分,就達到回收目標(biāo)了,不需要再進行新的一輪回收工作了。
但是由于漏統(tǒng)計了ion cached的回收部分,還得再多做一輪回收工作,直到回收夠32個page再結(jié)束。
原因
shrink_slab時,實際回收成功的文件系統(tǒng)緩存都可以被內(nèi)核內(nèi)存回收模塊統(tǒng)計到,但是ion cached這塊,可能是位于staging分支的緣故,內(nèi)核主線代碼對它的管理不是很到位而造成的。
二、 rmap查找的耗時
這個是目前內(nèi)核內(nèi)存回收中一個比較頭疼的性能問題。
1 問題現(xiàn)象
在android系統(tǒng)里面,用perf工具抓下kswapd的性能profile, 會發(fā)現(xiàn)內(nèi)核內(nèi)存回收這塊有很多工作花在了 page_referenced -> page_vma_mapped_walk這個地方了。
之前XXX的系統(tǒng)高負載時匿名頁回收性能調(diào)查博客里面,也提到了相機app進程會卡在shrink_page_list → page_referenced函數(shù)里面時間還比較長。
2 原因分析
1)內(nèi)核內(nèi)存回收搞了個二次機會法,增加了rmap反查耗時。
二次機會法意味著所有的內(nèi)存頁在進入inactive list后,幾乎都要做一遍page_check_references工作,這樣才能被真正回收掉。
這個地方的工作確實有必要,因為lru list只是保證了用戶進程先分配使用的內(nèi)存頁放到tail,后分配使用的內(nèi)存頁放到了head.
但是在系統(tǒng)運行過程中,如果放到了inactive list tail的內(nèi)存頁被進程再次訪問到,那么就會重新變回active,那么就得靠二次機會法來把它移到active list里面。
二次機會法靠這個page_check_references工作,來保證內(nèi)核內(nèi)存回收的都是最近不經(jīng)常被使用到的page.
2) android系統(tǒng)的共享頁很多,也增加了rmap反查耗時。
android里面的app進程都是從zygote進程fork出來的,并且app進程自身也會fork出很多線程。這樣造成了android里面共享頁的確很多,同一個page被共享進程的數(shù)目也很多。
這樣就會帶來page_referenced的反查耗時工作。
3) lru active list上也加了page_referenced的反查工作。
這部分反查工作一方面是為了recent_rotated計數(shù),最后是為了get_scan_count里面的確定file list和anon list的查找傾斜度。
高內(nèi)存壓力場合,get_scan_count里面也推薦用SCAN_EQUAL,而不是SCAN_FRACT,所以這種場合還是否有必要做上面傾斜度確定工作,需要再看看是否能進一步優(yōu)化下。
另外一方面主要是為了在active page被降級加入到inactive lru list之前,做下清除映射該page的各用戶態(tài)進程pte的referenced標(biāo)記工作。
因為要加入到inactive list上,至少說明此時該page已經(jīng)是stale page, 已經(jīng)被降級下來準(zhǔn)備要被回收了,所以應(yīng)該清除下映射該page的pte里面的referenced標(biāo)記了。
內(nèi)核內(nèi)存回收方面的一些梳理:
其實從這些內(nèi)存回收設(shè)計方面可以看出,linux內(nèi)存回收這塊還是基于一些經(jīng)驗主義的判斷,不可能做到特別準(zhǔn)確地識別出最久不被使用的內(nèi)存頁。
就是靠不斷地做rmap反查和一些經(jīng)驗主義判斷(文件頁先放到inactive list上,匿名頁先放到active list,內(nèi)核認為匿名頁都是比文件頁更活躍的)
來最大程度上保證hot page(即每個進程的working-set內(nèi)存頁)放到active list上,cold page(進程的非woking set內(nèi)存頁)放到inactive list上去。
其中在每個page被回收之前,匿名頁一般被反查2次,文件頁一般被反查3次,反查的確比較耗時。
不過linux kernel在性能方面是針對通用系統(tǒng),重點是為服務(wù)器系統(tǒng)設(shè)計的,所以嵌入式方面還是有不少特殊場景(比如相機高內(nèi)存峰值出現(xiàn)場景)可以做內(nèi)核內(nèi)存回收方面的性能優(yōu)化的。
3 優(yōu)化方法
或者用app compact回收單進程,或者借鑒下lwn上的這篇優(yōu)化文章:The multi-generational LRU,講的rmap反查的弊端和優(yōu)化方法。
或者我現(xiàn)在正在調(diào)研的思路:相機場景下,高效內(nèi)存回收思路。
如果要回收的內(nèi)存數(shù)量多,比如回收1 - 2G,那么其實可以識別出相機自身相關(guān)進程使用的內(nèi)存page后,對非相機使用的page可以減少上面rmap的反查工作, 理由如下:
1) 因為非相機進程即使發(fā)生page refault,也是僅僅影響的是非相機進程的性能。
2) 在主動內(nèi)存回收之前,內(nèi)核的全局lru鏈表中已經(jīng)大概率是一個比較成熟,比較完美地冷熱內(nèi)存頁分離的狀況了(hot page放在active list,cold page放在inactive list上),這樣充分利用內(nèi)核的lru list,先回收冷頁。
3) 最重要此時相機操作需要大量內(nèi)存,而系統(tǒng)處于內(nèi)存緊缺狀況,需要短時間內(nèi)回收出大量內(nèi)存出來。
而此時結(jié)合下面的調(diào)研,就算做上面耗時的rmap反查工作,也會很容易出現(xiàn)比如幾秒鐘前才被訪問過的頁面可能都不會被視作活躍,而被回收掉(因為此時回收過程中頁掃描會出現(xiàn)地異常頻繁,會加速頁面老化)。
那還不如不做rmap反查,直接回收算了。
三 、內(nèi)核匿名頁回收冷熱分離做的還不夠好,導(dǎo)致回收效率低
1匿名內(nèi)存和文件內(nèi)存的各自特點
1)匿名內(nèi)存
內(nèi)核認為匿名內(nèi)存大部分都是active的,不會有太多used once或者short-lived的匿名內(nèi)存。
就是說內(nèi)核假定:匿名內(nèi)存,比如malloc,一旦被用戶創(chuàng)建出來分配好對應(yīng)的物理內(nèi)存,那么該塊物理內(nèi)存就會被頻繁使用,如果不被用的話,用戶會及時free該內(nèi)存的(否則的話,就算是內(nèi)存泄漏了)。
但是相機場景下,不好說,應(yīng)該會存在一些并不是被頻繁使用的匿名內(nèi)存,用戶創(chuàng)建出來后,是為全局考慮的,會出現(xiàn)有一段時間會頻繁使用,使用完并不釋放,而是過一段稍微長時間后,還會繼續(xù)用它。
2)文件內(nèi)存
內(nèi)核假定文件內(nèi)存里面有相當(dāng)一部分是used-once或者short-lived內(nèi)存(因為文件預(yù)讀會引入很多不必要內(nèi)存頁),當(dāng)然也有另外一部分是被頻繁使用的內(nèi)存,兩部分交織在一塊。
2 內(nèi)核對文件頁和匿名頁在inactive/active分離方面的處理方式
1)匿名內(nèi)存
因為內(nèi)核認為匿名內(nèi)存普通都是hot/active的,只要該塊內(nèi)存存在,就會被頻繁用到的,所以把新創(chuàng)建的匿名頁加入到了active list上。
同時二次機會法也不適用它,在inactive list上,只要匿名page被用戶進程訪問到了,就會被立刻晉升到active list上去。
同時inactive_list_is_low函數(shù)里面也規(guī)定了active list長度要大于inactive的,另外重要的是匿名頁也沒有做working-set識別,轉(zhuǎn)換和保護。
2)文件內(nèi)存
上面說的其實是文件內(nèi)存里面都是inactive和active內(nèi)存頁混合在一起,所以內(nèi)核認為文件內(nèi)存不一定都是active的,所以新創(chuàng)建的文件內(nèi)存就會被加入到inactive list上去,
然后進行進一步地通過二次機會法來進行篩選,把頻繁訪問的,即active內(nèi)存晉升到active list上去,inactive的則被留下在內(nèi)存不足時優(yōu)先被回收掉。
這樣隨著事件的推移,進程的working-set內(nèi)存頁就會幾乎全部集中在active list,而不會出現(xiàn)used-once/short-lived這種inactive內(nèi)存跑到active list上,給進程內(nèi)存頁的冷熱分離帶來困難。
這樣進程的working-set內(nèi)存頁就會被建立起來,并且不容易被內(nèi)存不足時回收掉。
另外進程的working-set在特定時間段內(nèi)是固定的,但是時間長了,肯定會切換到另外一個working-set里面。
在working-set切換時,文件頁有refault distance算法來防止出現(xiàn)切換后,頻繁出現(xiàn)page thrashing.
3 最后結(jié)論
文件頁回收有很多優(yōu)化算法在保證盡量做到冷熱分離和減小page thrashing,但是匿名頁卻沒有這些算法保證,所以匿名頁回收效率感覺比文件頁低,導(dǎo)致的一個不好影響就是匿名頁發(fā)生的page thrashing應(yīng)該比較多于文件頁。
具體僅僅這里是通過代碼分析發(fā)現(xiàn)的,還得通過實驗數(shù)據(jù)驗證。
四、 內(nèi)核的內(nèi)存回收從來都是被動的回收
1 具體特點
1) 內(nèi)存回收都是內(nèi)存分配不能滿足需求時,才不得不陷入到slow path分配中做回收,這樣對內(nèi)存分配性能是會有差的影響的。
2) 就算陷入到slow path中,觸發(fā)的kswapd線程,也是在balance式地回收,只要內(nèi)存水位稍微達到一定閥值,內(nèi)存處于balanced狀態(tài)后,就停止工作了。
3) 內(nèi)存頁面的老化,只有在lru list被scan時,才會老化。也就是說LRU里面的老化時間流逝跟自然時間是沒有關(guān)系的。
掃描才是推動歷史車輪前進的動力。而掃描又是由于達不到balanced而被觸發(fā)的,可見頁面老化的速度跟系統(tǒng)中內(nèi)存的緊缺程度是相關(guān)的。
內(nèi)存緊缺的時候,1分鐘前才被訪問過的頁面可能都不會被視作活躍;反過來,如果內(nèi)存不緊缺,長期不需要進行回收,那么幾小時前訪問過的頁面又可能都會被視作活躍,并且在這段時間內(nèi)被訪問過一次和被訪問過多次的頁面會被同等對待
看過內(nèi)存回收二次機會法,才會對這個點有深入理解。
4) 目前的防止出現(xiàn)內(nèi)存thrashing而做的workingset保護,只是保護了文件頁,匿名頁還未進行保護(好像內(nèi)核5.9版本上才有)。其實匿名頁也需要進行workingset保護,減小thrashing。
2 導(dǎo)致的問題
移動設(shè)備相機場景下,經(jīng)常有突發(fā)性的高峰值內(nèi)存分配需求出現(xiàn)。如果還是上面這種內(nèi)存回收特點的話,是會導(dǎo)致相機內(nèi)存分配性能差,從而出現(xiàn)用戶操作卡頓,殺后臺現(xiàn)象比較嚴重。
3 解決方法
制定相機場景下的殺進程管理策略,還有主動內(nèi)存回收策略,從而釋放整機內(nèi)存壓力,優(yōu)化相機內(nèi)存分配性能。
審核編輯:劉清
-
嵌入式系統(tǒng)
+關(guān)注
關(guān)注
41文章
3534瀏覽量
128995 -
cpu
+關(guān)注
關(guān)注
68文章
10780瀏覽量
210503 -
LINUX內(nèi)核
+關(guān)注
關(guān)注
1文章
316瀏覽量
21583
原文標(biāo)題:使用內(nèi)存回收技術(shù)對嵌入式系統(tǒng)進行性能優(yōu)化
文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論