【1】內(nèi)存映射
Linux 內(nèi)核給每個進程都提供了一個獨立且連續(xù)的虛擬地址空間,以便進程可以方便地訪問虛擬內(nèi)存;虛擬地址空間的內(nèi)部又被分為內(nèi)核空間和用戶空間兩部分,不同字長的處理器,地址空間的范圍也不同;圖示為 32 位和 64 位系統(tǒng)的虛擬地址空間;
內(nèi)存映射是將虛擬內(nèi)存地址映射到物理內(nèi)存地址,內(nèi)核為每個進程都維護了一張頁表,記錄虛擬地址與物理地址的映射關系;
頁表存儲在 CPU 的內(nèi)存管理單元 MMU 中,正常情況下,處理器就可以直接通過硬件,找出要訪問的內(nèi)存;當進程訪問的虛擬地址在頁表中不存在時,系統(tǒng)會產(chǎn)生一個缺頁異常,進入內(nèi)核空間分配物理內(nèi)存、更新進程頁表,最后再返回用戶空間,恢復進程的運行;
TLB (Translation Lookaside Buffer,轉(zhuǎn)譯后備緩沖器) 是 MMU 中頁表的高速緩存,由于進程的虛擬地址空間是獨立的,而 TLB 的訪問速度又比 MMU 快得多,因此通過減少進程的上下文切換,減少 TLB 的刷新次數(shù),可以提高 TLB 緩存的使用率,進而提高 CPU 的內(nèi)存訪問性能;
MMU 規(guī)定了內(nèi)存映射的最小單位,即頁,通常是 4 KB 大小,每一次內(nèi)存映射,都需要關聯(lián) 4KB 或者 4KB 整數(shù)倍的內(nèi)存空間;
多級頁表就是把內(nèi)存分成區(qū)塊來管理,將原來的映射關系改成區(qū)塊索引和區(qū)塊內(nèi)的偏移;由于虛擬內(nèi)存空間通常只用了很少一部分,多級頁表就只保存這些使用中的區(qū)塊,從而大大地減少頁表的項數(shù),Linux 四級頁表管理內(nèi)存頁圖示
大頁,即比普通頁更大的內(nèi)存塊,常見的大小有 2MB 和 1GB;
【2】虛擬內(nèi)存空間分布
32 位系統(tǒng)中用戶空間的分段示意圖
- 只讀段,包括代碼和常量等
- 數(shù)據(jù)段,包括全局變量等
- 堆,包括動態(tài)分配的內(nèi)存,從低地址開始向上增長
- 文件映射段,包括動態(tài)庫、共享內(nèi)存等,從高地址開始向下增長
- 棧,包括局部變量和函數(shù)調(diào)用的上下文等,棧的大小是固定的,一般是 8 MB
【3】內(nèi)存的分配與回收
內(nèi)存分配
小塊內(nèi)存 (小于 128K),使用 brk() 來分配,即通過移動堆頂?shù)奈恢脕矸峙鋬?nèi)存,這些內(nèi)存釋放后并不會立刻歸還系統(tǒng),而是被緩存起來,以便重復使用;
- brk() 方式分配內(nèi)存,可以減少缺頁異常的發(fā)生,提高內(nèi)存訪問效率;由于這些內(nèi)存沒有歸還系統(tǒng),在內(nèi)存工作繁忙時,頻繁的內(nèi)存分配和釋放會造成內(nèi)存碎片;
大塊內(nèi)存 (大于 128K),使用內(nèi)存映射 mmap() 來分配,即在文件映射段找一塊空閑內(nèi)存分配出去
- mmap() 方式分配內(nèi)存,會在釋放時直接歸還系統(tǒng),因此 mmap 會發(fā)生缺頁異常;在內(nèi)存工作繁忙時,頻繁的內(nèi)存分配會導致大量的缺頁異常,使內(nèi)核的管理負擔增大;
內(nèi)存回收
回收緩存,如使用 LRU (Least Recently Used) 算法,回收最近使用最少的內(nèi)存頁面;
回收不常訪問的內(nèi)存,把不常用的內(nèi)存通過交換分區(qū)直接寫到磁盤中;
- 交換分區(qū) (Swap) 即把一塊磁盤空間當成內(nèi)存來用;把進程暫時不用的數(shù)據(jù)存儲到磁盤中(換出),當進程訪問這些內(nèi)存時,再從磁盤讀取這些數(shù)據(jù)到內(nèi)存中 (換入)
殺死進程,內(nèi)存緊張時系統(tǒng)還會通過 OOM (Out of Memory),直接殺掉占用大量內(nèi)存的進程;
內(nèi)核的一種保護機制,監(jiān)控進程的內(nèi)存使用情況,并且使用 oom_score 為每個進程的內(nèi)存使用情況進行評分;
- 一個進程消耗的內(nèi)存越大,oom_score 就越大;
- 一個進程運行占用的 CPU 越多,oom_score 就越??;
- 管理員可以通過 /proc 文件系統(tǒng),手動設置進程的 oom_adj,從而調(diào)整進程的 oom_score;
oom_adj 的范圍是 [-17, 15],數(shù)值越大,表示進程越容易被 OOM 殺死;數(shù)值越小,表示進程越不容易被 OOM 殺死,其中 -17 表示禁止 OOM;
【4】buffer/cache
Buffer 是內(nèi)核緩沖區(qū)用到的內(nèi)存,對應的是 /proc/meminfo 中的 Buffers 值;
Cache 是內(nèi)核頁緩存和 Slab 用到的內(nèi)存,對應的是 /proc/meminfo 中的 Cached 與 SReclaimable 之和;
- Buffers 是對原始磁盤塊的臨時存儲,即用來緩存磁盤的數(shù)據(jù),通常不會特別大 (20MB 左右);從而內(nèi)核可以把分散的寫集中起來,統(tǒng)一優(yōu)化磁盤的寫入,比如可以把多次小的寫合并成單次大的寫等等;
Buffer 既可以用作 “將要寫入磁盤數(shù)據(jù)的緩存”,也可以用作 “從磁盤讀取數(shù)據(jù)的緩存”
- Cached 是從磁盤讀取文件的頁緩存,即用來緩存從文件讀取的數(shù)據(jù);從而,下次訪問這些文件數(shù)據(jù)時,就可以直接從內(nèi)存中快速獲取,而不需要再次訪問緩慢的磁盤;
實際上,Cache 也會緩存寫文件時的數(shù)據(jù)
Cache 既可以用作 “從文件讀取數(shù)據(jù)的頁緩存”,也可以用作 “寫文件的頁緩存”
- SReclaimable 是 Slab 的一部分,Slab 包括兩部分,其中的可回收部分,用 SReclaimable 記錄;而不可回收部分,用 SUnreclaim 記錄;
【5】內(nèi)存泄漏
棧內(nèi)存由系統(tǒng)自動分配和管理,一旦程序運行超出了這個局部變量的作用域,棧內(nèi)存就會被系統(tǒng)自動回收,不會產(chǎn)生內(nèi)存泄漏的問題;
堆內(nèi)存由應用程序分配和管理,除非程序退出,這些堆內(nèi)存并不會被系統(tǒng)自動釋放,而是需要應用程序明確調(diào)用庫函數(shù) free() 釋放,如果應用程序沒有正確釋放堆內(nèi)存,就會造成內(nèi)存泄漏;
只讀段,包括程序的代碼和常量,由于是只讀的,不會再去分配新的內(nèi)存,不會產(chǎn)生內(nèi)存泄漏;
數(shù)據(jù)段,包括全局變量和靜態(tài)變量,這些變量在定義時就已經(jīng)確定了大小,不會產(chǎn)生內(nèi)存泄漏;
內(nèi)存映射段,包括動態(tài)鏈接庫和共享內(nèi)存,其中共享內(nèi)存由程序動態(tài)分配和管理,若程序在分配后忘了回收,就會導致泄漏問題;
【6】Swap 知識點
Swap 即把一塊磁盤空間或者一個本地文件當成內(nèi)存來使用,包括換出和換入兩個過程;
- 換出,即把進程暫時不用的內(nèi)存數(shù)據(jù)存儲到磁盤中,并釋放這些數(shù)據(jù)占用的內(nèi)存;
- 換入,即在進程再次訪問這些內(nèi)存的時候,把它們從磁盤讀到內(nèi)存中來;
NUMA 與 Swap
NUMA (Non-Uniform Memory Access) 架構,在 NUMA 架構下,多個處理器被劃分到不同 Node 上,且每個 Node 都擁有自己的本地內(nèi)存空間,而同一個 Node 內(nèi)部的內(nèi)存空間,又可以進一步分為不同的內(nèi)存域(Zone);
某個 Node 內(nèi)存不足時,系統(tǒng)可以從其他 Node 尋找空閑內(nèi)存,也可以從本地內(nèi)存中回收內(nèi)存;可以通過
/proc/sys/vm/zone_reclaim_mode 來選擇模式,支持以下幾個選項;
- 默認的 0,表示既可以從其他 Node 尋找空閑內(nèi)存,也可以從本地回收內(nèi)存;
- 1、2、4 都表示只回收本地內(nèi)存,2 表示可以回寫臟數(shù)據(jù)回收內(nèi)存,4 表示可以用 Swap 方式回收內(nèi)存;
swappiness
- 對文件頁的回收,即直接回收緩存,或者把臟頁寫回磁盤后再回收;
- 對匿名頁的回收,即通過 Swap 機制,把它們寫入磁盤后再釋放內(nèi)存;
Linux 提供了 /proc/sys/vm/swappiness 選項,用來調(diào)整使用 Swap 的積極程度,swappiness 的范圍是 0-100,數(shù)值越大,越積極使用 Swap,即更傾向于回收匿名頁;數(shù)值越小,越消極使用 Swap,即更傾向于回收文件頁;
降低 Swap 的使用,可以提高系統(tǒng)的整體性能
- 禁止 Swap,現(xiàn)在服務器的內(nèi)存足夠大,所以除非有必要,禁用 Swap 即可,隨著云計算的普及,大部分云平臺中的虛擬機都默認禁止 Swap;
- 若實在需要用到 Swap,可以嘗試降低 swappiness 的值,減少內(nèi)存回收時 Swap 的使用傾向;
- 響應延遲敏感的應用,如果它們可能在開啟 Swap 的服務器中運行,你還可以用庫函數(shù) mlock() 或者 mlockall() 鎖定內(nèi)存,阻止它們的內(nèi)存換出;
【7】Linux 回收內(nèi)存的時機
在內(nèi)存資源緊張時,Linux 通過直接內(nèi)存回收和定期掃描的方式,釋放文件頁和匿名頁,以便把內(nèi)存分配給更需要的進程使用;
直接內(nèi)存回收,存在新的大塊內(nèi)存分配請求,但是剩余內(nèi)存不足,此時系統(tǒng)就需要回收一部分內(nèi)存,進而盡可能地滿足新內(nèi)存請求;
kswapd0 內(nèi)核線程,用于定期回收內(nèi)存,kswapd0 定義了三個內(nèi)存閾值,分別是頁最小閾值 (pages_min)、頁低閾值(pages_low) 和頁高閾值(pages_high);pages_free 表示剩余內(nèi)存;
- 剩余內(nèi)存小于頁最小閾值,說明進程可用內(nèi)存都耗盡了,只有內(nèi)核才可以分配內(nèi)存;
- 剩余內(nèi)存落在頁最小閾值和頁低閾值中間,說明內(nèi)存壓力比較大,剩余內(nèi)存不多了;這時 kswapd0 會執(zhí)行內(nèi)存回收,直到剩余內(nèi)存大于高閾值為止;
- 剩余內(nèi)存落在頁低閾值和頁高閾值中間,說明內(nèi)存有一定壓力,但還可以滿足新內(nèi)存請求;
- 剩余內(nèi)存大于頁高閾值,說明剩余內(nèi)存比較多,沒有內(nèi)存壓力;
頁低閾值可以通過內(nèi)核選項
/proc/sys/vm/min_free_kbytes 來間接設置,min_free_kbytes 設置了頁最小閾值,而其他兩個閾值,都是根據(jù)頁最小閾值計算生成,如下
pages_low = pages_min * 5 / 4
pages_high = pages_min * 3 / 2
性能指標與工具總結(jié)
實戰(zhàn)記錄
【1】free 命令
- total 總內(nèi)存大??;
- used 已使用內(nèi)存的大小,包含了共享內(nèi)存;
- free 未使用內(nèi)存的大小;
- shared 共享內(nèi)存的大??;
- buff/cache 緩存和緩沖區(qū)的大??;
- available 新進程可用內(nèi)存的大??;
【2】top 命令
VIRT 是進程虛擬內(nèi)存的大小,只要是進程申請過的內(nèi)存,即便還沒有真正分配物理內(nèi)存,也會計算在內(nèi);
RES 是常駐內(nèi)存的大小,即進程實際使用的物理內(nèi)存大小,但不包括 Swap 和共享內(nèi)存;
SHR 是共享內(nèi)存的大小,比如與其他進程共同使用的共享內(nèi)存、加載的動態(tài)鏈接庫以及程序的代碼段等;
%MEM 是進程使用物理內(nèi)存占系統(tǒng)總內(nèi)存的百分比;
注意
- 虛擬內(nèi)存通常并不會全部分配物理內(nèi)存;
- 共享內(nèi)存 SHR 并不一定是共享的,如程序的代碼段、非共享的動態(tài)鏈接庫,也都算在 SHR 里;
【3】磁盤和文件寫案例
【3.1】案例一,寫文件
測試命令
$ dd if=/dev/urandom of=/tmp/file bs=1M count=500
監(jiān)控命令
vmstat 2
- 在 Cache 剛開始增長時,塊設備 I/O 很少,而過一段時間后,才會出現(xiàn)大量的塊設備寫;
- 當 dd 命令結(jié)束后,Cache 不再增長,但塊設備寫還會持續(xù)一段時間,并且多次 I/O 寫的結(jié)果加起來是 dd 要寫的 500M 的數(shù)據(jù);
【3.2】案例二,寫磁盤
測試命令
# 運行dd命令向磁盤分區(qū)/dev/sdb1寫入2G數(shù)據(jù)
$ dd if=/dev/urandom of=/dev/sdb1 bs=1M count=2048
監(jiān)控命令
vmstat 2
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 7584780 153592 97436 0 0 684 0 31 423 1 48 50 2 0
1 0 0 7418580 315384 101668 0 0 0 0 32 144 0 50 50 0 0
1 0 0 7253664 475844 106208 0 0 0 0 20 137 0 50 50 0 0
1 0 0 7093352 631800 110520 0 0 0 0 23 223 0 50 50 0 0
1 1 0 6930056 790520 114980 0 0 0 12804 23 168 0 50 42 9 0
1 0 0 6757204 949240 119396 0 0 0 183804 24 191 0 53 26 21 0
1 1 0 6591516 1107960 123840 0 0 0 77316 22 232 0 52 16 33 0
- 寫磁盤時 (即 bo 大于 0 時),Buffer 和 Cache 都在增長,但顯然 Buffer 的增長快得多;
【4】磁盤和文件讀案例
【4.1】案例一、讀文件
測試命令
# 運行dd命令讀取文件數(shù)據(jù)
$ dd if=/tmp/file of=/dev/null
監(jiān)控命令
vmstat 2
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 1 0 7724164 2380 110844 0 0 16576 0 62 360 2 2 76 21 0
0 1 0 7691544 2380 143472 0 0 32640 0 46 439 1 3 50 46 0
0 1 0 7658736 2380 176204 0 0 32640 0 54 407 1 4 50 46 0
0 1 0 7626052 2380 208908 0 0 32640 40 44 422 2 2 50 46 0
- 讀取文件時 (即 bi 大于 0 時),Buffer 保持不變,而 Cache 則在不停增長;
【4.2】案例二、讀磁盤
測試命令
# 運行dd命令讀取文件
$ dd if=/dev/sda1 of=/dev/null bs=1M count=1024
監(jiān)控命令
vmstat 2
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 0 7225880 2716 608184 0 0 0 0 48 159 0 0 100 0 0
0 1 0 7199420 28644 608228 0 0 25928 0 60 252 0 1 65 35 0
0 1 0 7167092 60900 608312 0 0 32256 0 54 269 0 1 50 49 0
0 1 0 7134416 93572 608376 0 0 32672 0 53 253 0 0 51 49 0
0 1 0 7101484 126320 608480 0 0 32748 0 80 414 0 1 50 49 0
- 讀磁盤時 (也就是 bi 大于 0 時),Buffer 和 Cache 都在增長,但顯然 Buffer 的增長快很多;
附錄
【1】文件系統(tǒng)與磁盤的區(qū)別
磁盤是一個存儲設備(塊設備),可以被劃分為不同的磁盤分區(qū),而在磁盤或者磁盤分區(qū)上,還可以再創(chuàng)建文件系統(tǒng),并掛載到系統(tǒng)的某個目錄中,這樣,系統(tǒng)就可以通過這個掛載目錄,來讀寫文件;即磁盤是存儲數(shù)據(jù)的塊設備,也是文件系統(tǒng)的載體;文件系統(tǒng)需要通過磁盤,來保證數(shù)據(jù)的持久化存儲;
在讀寫普通文件時,I/O 請求會首先經(jīng)過文件系統(tǒng),然后由文件系統(tǒng)負責,來與磁盤進行交互;在讀寫塊設備文件時,會跳過文件系統(tǒng),直接與磁盤交互,也就是所謂的 “裸 I/O”;
【2】統(tǒng)計所有進程的物理內(nèi)存使用量
每個進程的 PSS ,是指把共享內(nèi)存平分到各個進程后,再加上進程本身的非共享內(nèi)存大小的和;
# 使用grep查找Pss指標后,再用awk計算累加值
$ grep Pss /proc/[1-9]*/smaps | awk '{total+=$2}; END {printf "%d kBn", total }'
391266 kB
-
處理器
+關注
關注
68文章
19286瀏覽量
229852 -
Linux
+關注
關注
87文章
11304瀏覽量
209519 -
內(nèi)存
+關注
關注
8文章
3025瀏覽量
74054 -
緩存
+關注
關注
1文章
240瀏覽量
26679
發(fā)布評論請先 登錄
相關推薦
評論