內(nèi)核和處理器負(fù)責(zé)將虛擬內(nèi)存映射到物理內(nèi)存。為了提高效率,會(huì)在稱為頁(yè)面的內(nèi)存組中創(chuàng)建內(nèi)存映射,其中每個(gè)頁(yè)面的大小是處理器的詳細(xì)信息。盡管大多數(shù)處理器也支持更大的容量,但通常有4 KB,Linux稱其為 hugepage大頁(yè)面。內(nèi)核可以從其自己的空閑列表中為物理內(nèi)存頁(yè)面請(qǐng)求提供服務(wù),內(nèi)核為每個(gè)DRAM組和CPU維護(hù)這些請(qǐng)求以提高效率。內(nèi)核自己的軟件也通常通過內(nèi)核分配器(例如slab分配器)從這些空閑列表中消耗內(nèi)存。
10余年的翼城網(wǎng)站建設(shè)經(jīng)驗(yàn),針對(duì)設(shè)計(jì)、前端、開發(fā)、售后、文案、推廣等六對(duì)一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。營(yíng)銷型網(wǎng)站建設(shè)的優(yōu)勢(shì)是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動(dòng)調(diào)整翼城建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無(wú)論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。創(chuàng)新互聯(lián)建站從事“翼城網(wǎng)站設(shè)計(jì)”,“翼城網(wǎng)站推廣”以來,每個(gè)客戶項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。
內(nèi)存頁(yè)和交換
典型的用戶內(nèi)存頁(yè)面的生命周期如圖7-2所示,其中列舉了以下步驟:
1. 應(yīng)用程序從內(nèi)存分配請(qǐng)求開始(例如,libc malloc() )。
2. 分配庫(kù)可以從其自己的空閑列表中為內(nèi)存請(qǐng)求提供服務(wù),或者它可能需要擴(kuò)展虛擬內(nèi)存來容納。根據(jù)分配庫(kù),它將:
1. 通過調(diào)用brk() syscall并將堆內(nèi)存用于分配來擴(kuò)展堆的大小。
2. 通過mmap()系統(tǒng)調(diào)用創(chuàng)建一個(gè)新的內(nèi)存段。
3. 稍后,應(yīng)用程序嘗試通過存儲(chǔ)和加載指令使用分配的內(nèi)存范圍,這涉及調(diào)用處理器內(nèi)存管理單元(MMU)進(jìn)行虛擬到物理地址的轉(zhuǎn)換。至此,虛擬內(nèi)存的謊言就暴露出來了:該地址沒有映射!這會(huì)導(dǎo)致稱為頁(yè)面錯(cuò)誤的MMU錯(cuò)誤。
4. 頁(yè)面錯(cuò)誤由內(nèi)核處理,內(nèi)核建立從其物理內(nèi)存可用列表到虛擬內(nèi)存的映射,然后將該映射通知MMU以供以后查找?,F(xiàn)在,該過程占用了額外的物理內(nèi)存頁(yè)面。進(jìn)程使用的物理內(nèi)存量稱為其駐留集大小(RSS)。
5. 當(dāng)系統(tǒng)上的內(nèi)存需求過多時(shí),內(nèi)核頁(yè)面輸出守護(hù)程序(kswapd)可能會(huì)尋找可用的內(nèi)存頁(yè)面。它將釋放三種類型的內(nèi)存中的一種(盡管只有(c)如圖7-2所示,因?yàn)樗@示了用戶內(nèi)存頁(yè)面的生命周期):
1. 從磁盤讀取但未修改的文件系統(tǒng)頁(yè)面(稱為“由磁盤支持”):可以立即釋放這些頁(yè)面,并在需要時(shí)簡(jiǎn)單地重新讀取。這些頁(yè)面是應(yīng)用程序可執(zhí)行的文本,數(shù)據(jù)和文件系統(tǒng)元數(shù)據(jù)。
2. 已修改的文件系統(tǒng)頁(yè)面:這些是“臟”的,必須先寫入磁盤,然后才能釋放它們。
3. 應(yīng)用程序內(nèi)存頁(yè)面:由于它們沒有文件來源,因此被稱為匿名內(nèi)存。如果正在使用交換設(shè)備,則可以先將它們存儲(chǔ)在交換設(shè)備上來釋放它們。將頁(yè)面寫到交換設(shè)備稱為交換(在Linux上)。
內(nèi)存分配請(qǐng)求通常是頻繁的活動(dòng):對(duì)于繁忙的應(yīng)用程序,用戶級(jí)別的分配每秒可能發(fā)生數(shù)百萬(wàn)次。加載和存儲(chǔ)指令以及MMU查找更加頻繁。它們每秒可能發(fā)生數(shù)十億次。在圖7-2中,這些箭頭以粗體顯示。其他活動(dòng)相對(duì)較少:brk()和mmap()調(diào)用,頁(yè)面錯(cuò)誤和頁(yè)面退出(較亮的箭頭)。
page-out daemon頁(yè)面輸出守護(hù)程序
定期激活頁(yè)面輸出守護(hù)程序(kswapd)以掃描非活動(dòng)和活動(dòng)頁(yè)面的LRU列表,以尋找可用的內(nèi)存。如圖7-3所示,當(dāng)空閑內(nèi)存越過低閾值時(shí)它將被喚醒,而當(dāng)空閑內(nèi)存越過高閾值時(shí)將回到睡眠狀態(tài)。
kswapd協(xié)調(diào)后臺(tái)頁(yè)面調(diào)出;除了CPU和磁盤I/O爭(zhēng)用外,這些不應(yīng)直接損害應(yīng)用程序性能。如果kswapd無(wú)法足夠快地釋放內(nèi)存,則會(huì)超過可調(diào)的最小頁(yè)面閾值,并使用直接回收;這是釋放內(nèi)存以滿足分配條件的前臺(tái)模式。在這種模式下,分配阻塞(停頓)并同步等待頁(yè)面被釋放。
直接回收可以調(diào)用內(nèi)核模塊收縮器函數(shù):這些釋放的內(nèi)存可能保留在緩存中的內(nèi)存,包括內(nèi)核slab緩存。
swap devices交換設(shè)備
交換設(shè)備為內(nèi)存不足的系統(tǒng)提供了降級(jí)的操作模式:進(jìn)程可以繼續(xù)分配,但是現(xiàn)在將不常使用的頁(yè)面移入和移出交換設(shè)備,這通常會(huì)使應(yīng)用程序運(yùn)行得慢得多。
一些生產(chǎn)系統(tǒng)無(wú)需交換即可運(yùn)行;這樣做的理由是,對(duì)于那些關(guān)鍵系統(tǒng)來說,降級(jí)的操作模式是永遠(yuǎn)無(wú)法接受的,因?yàn)檫@些關(guān)鍵系統(tǒng)可能有許多冗余(且運(yùn)行狀況良好)服務(wù)器,比開始交換的服務(wù)器要好用得多。(例如,對(duì)于Netflix云實(shí)例,通常就是這種情況。)
如果無(wú)交換系統(tǒng)的內(nèi)存不足,則內(nèi)核oom killer會(huì)犧牲一個(gè)進(jìn)程。為了避免這種情況,將應(yīng)用程序配置為永不超過系統(tǒng)的內(nèi)存限制。
oom killer
Linux內(nèi)存不足殺手是釋放內(nèi)存的最后手段:它將使用啟發(fā)式方法找到受害者進(jìn)程,并通過殺死它們來犧牲它們。啟發(fā)式尋找將釋放許多頁(yè)面的最大受害者,并且這不是關(guān)鍵任務(wù),例如內(nèi)核線程或init(PID 1)。Linux提供了在整個(gè)系統(tǒng)和每個(gè)進(jìn)程中調(diào)整OOM殺手的行為的方法。
page compaction頁(yè)面壓縮
隨著時(shí)間的流逝,釋放的頁(yè)面變得碎片化,從而使內(nèi)核很難根據(jù)需要分配較大的連續(xù)塊。內(nèi)核使用壓縮程序來移動(dòng)頁(yè)面,從而釋放連續(xù)區(qū)域。
file system caching and buffering文件系統(tǒng)緩存和緩沖
Linux借用空閑內(nèi)存進(jìn)行文件系統(tǒng)緩存,并在有需求時(shí)將其恢復(fù)為空閑狀態(tài)。這種借用的結(jié)果是,在Linux啟動(dòng)之后,系統(tǒng)報(bào)告的可用內(nèi)存趨向于零,這可能使用戶擔(dān)心系統(tǒng)實(shí)際上只是在預(yù)熱其文件系統(tǒng)緩存時(shí)會(huì)耗盡內(nèi)存。此外,文件系統(tǒng)使用內(nèi)存進(jìn)行回寫緩沖(write-back buffering)。
可以將Linux調(diào)整為更喜歡從文件系統(tǒng)緩存中釋放或通過交換釋放內(nèi)存(通過調(diào)整參數(shù)vm.swappiness)。
傳統(tǒng)的分析工具
傳統(tǒng)的性能工具提供了許多基于容量的內(nèi)存使用情況統(tǒng)計(jì)信息,包括每個(gè)進(jìn)程和系統(tǒng)范圍內(nèi)使用了多少虛擬和物理內(nèi)存,以及某些細(xì)分,例如按流程段或面板。分析內(nèi)存使用率超出基本知識(shí),例如頁(yè)面錯(cuò)誤率,分配庫(kù),運(yùn)行時(shí)或應(yīng)用程序?qū)γ總€(gè)分配都需要內(nèi)置的工具;或者可以使用像Valgrind這樣的虛擬機(jī)分析器;后一種方法可能會(huì)導(dǎo)致目標(biāo)應(yīng)用程序在檢測(cè)時(shí)運(yùn)行速度慢10倍以上。BPF工具效率更高,開銷也更小。
Tool | Type | Description |
dmesg | Kernel log | OOM killer event details |
swapon | Kernel statistics | Swap device usage |
free | Kernel statistics | System-wide memory usage |
ps | Kernel statistics | Process statistics, including memory usage |
pmap | Kernel statistics | Process memory usage by segment |
vmstat | Kernel statistics | Various statistics, including memory |
sar | Kernel statistics | Can show page fault and page scanner rates |
perf | Software events, hardware statistics, hardware sampling | Memory-related PMC statistics and event sampling |
用于內(nèi)存分析相關(guān)的BPF工具
內(nèi)存相關(guān)的工具:
Tool | Source | Target | Description |
oomkill | BCC/BT | OOM | Shows extra info on OOM kill events 顯示oom相關(guān)的事件 |
memleak | BCC | Sched | Shows possible memory leak code paths 顯示可能的內(nèi)存泄漏代碼路徑 |
mmapsnoop | Book | Syscalls | Traces mmap(2) calls system-wide 跟蹤系統(tǒng)范圍內(nèi)的mmap調(diào)用 |
brkstack | Book | Syscalls | Shows brk() calls with user stack traces 顯示帶有用戶堆棧跟蹤的brk()調(diào)用 |
shmsnoop | BCC | Syscalls | Traces shared memory calls with details 跟蹤共享內(nèi)存調(diào)用的詳細(xì)信息 |
faults | Book | Faults | Shows page faults, by user stack trace 通過用戶堆棧跟蹤顯示頁(yè)面錯(cuò)誤 |
ffaults | Book | Faults | Shows page faults, by filename 通過文件名顯示頁(yè)面錯(cuò)誤 |
vmscan | Book | VM | Measures VM scanner shrink and reclaim times 測(cè)量vm scaner的收縮和回收時(shí)間 |
drsnoop | BCC | VM | Traces direct reclaim events, showing latency 跟蹤直接回收事件,顯示延遲 |
swapin | Book | VM | Shows swap-ins by process 按進(jìn)程顯示swap情況 |
hfaults | Book | Faults | Shows huge page faults, by process 按進(jìn)程顯示巨頁(yè)錯(cuò)誤情況 |
此外,還有幾個(gè)用于內(nèi)存分析的BPF工具: kmem、kpages、 slabratetop、 numamove
oomkill是一個(gè)BCC和bpftrace工具,用于跟蹤內(nèi)存不足殺手事件并打印詳細(xì)信息(例如平均負(fù)載)。平均負(fù)載為OOM時(shí)的系統(tǒng)狀態(tài)提供了一些額外的上下文,顯示了系統(tǒng)是否正在變得忙碌或穩(wěn)定。
此輸出表明PID 18601(perl)需要內(nèi)存,這觸發(fā)了PID 1165(java)的OOM終止。PID 1165的內(nèi)存占用已達(dá)到18006224個(gè)pages;這些通常每頁(yè)4 KB,具體取決于處理器和進(jìn)程內(nèi)存設(shè)置。loadavg平均負(fù)載表明,在OOM終止時(shí),系統(tǒng)變得更加繁忙。
該工具通過使用kprobes跟蹤oom_kill_process()函數(shù)并打印各種細(xì)節(jié)來工作。在這種情況下,只需讀取/proc/loadavg即可獲取平均負(fù)載。調(diào)試OOM事件時(shí),可以根據(jù)需要增強(qiáng)此工具以打印其他詳細(xì)信息。此外,此工具尚未使用可以顯示有關(guān)如何選擇任務(wù)的更多詳細(xì)信息的oom跟蹤點(diǎn)。
memleak是一個(gè)BCC工具,可跟蹤內(nèi)存分配和空閑事件以及分配堆棧跟蹤。隨著時(shí)間的流逝,它可以顯示長(zhǎng)期幸存者-尚未釋放的分配。
此示例顯示了在bash shell進(jìn)程上運(yùn)行的memleak:
僅memleak不能告訴您這些分配是否是真正的內(nèi)存泄漏(內(nèi)存泄漏:指的是沒有引用并且永遠(yuǎn)不會(huì)釋放的已分配內(nèi)存),內(nèi)存增長(zhǎng)還是長(zhǎng)期分配。為了區(qū)分它們,需要研究和理解代碼路徑。