LInux性能優化--內存(1基礎篇幅)

平常買電腦的時候都會關注一個指標–內存。
內存的作用是什麼呢?
平常我們使用的程序都是安裝的硬盤上的,CPU要運行程序必須要先載入到內存裏面。這是因爲CPU的速度是遠遠高於硬盤的存取速度,爲了效率使用了內存,存儲層次架構如下圖:
在這裏插入圖片描述
內存都是內核來管理的,那麼應用程序是怎麼使用內存的?
內存映射
Linux 內核給每個進程都提供了一個獨立的虛擬地址空間,並且這個地址空間是連續的。那麼應用程序就可以很方便的使用了內存了,這一段內存叫做虛擬內存。虛擬內存意思就是不是真實的物理內存,那麼內核就要負責把虛擬內存映射到真實的物理內存。
虛擬內存也是有格式的:
在這裏插入圖片描述
可以看出,32 位系統的內核空間佔用 1G,位於最高處,剩下的 3G 是用戶空間。而 64 位系統的內核空間和用戶空間都是 128T,分別佔據整個內存空間的最高和最低處,剩下的中間部分是未定義的。應用程序是不能直接訪問內核空間的,否則會出現段錯誤,必須要切換到內核態。內核空間都會映射到同一塊物理內存上,這樣就方便應用程序切換到內核態。
那麼內存映射是怎麼實現的呢?
在這裏插入圖片描述
內核通過頁表實現了虛擬內存到物理內存的映射。當操作系統在頁表找不到虛擬內存的時候,就會產生一個缺頁異常,進入內核空間分配物理內存、更新進程頁表,最後再返回用戶空間,恢復進程的運行。頁表存儲在MMU中,頁表的單位是4KB或者4KB整數倍爲單位的。還有一個TLB(後備緩衝區),速度高於MMU的。所以頻繁進行進程上下文切換的時候,TLB的緩存會失效,通過減少進程的上下文切換,減少 TLB 的刷新次數,就可以提高 TLB 緩存的使用率,進而提高 CPU 的內存訪問性能。
這裏有一個問題,比如在32位的系統上,系統就需要 100 多萬個頁表項(4GB/4KB)纔可以實現整個地址空間的映射。這個頁表會變得非常巨大。爲了解決這一個問題,LINUX使用了兩種機制,多級頁表和大頁。
多級頁表:
多級頁表就是把內存分成區塊來管理,將原來的映射關係改成區塊索引和區塊內的偏移。由於虛擬內存空間通常只用了很少一部分,那麼,多級頁表就只保存這些使用中的區塊,這樣就可以大大地減少頁表的項數。
LINUX使用的是4級頁表。
在這裏插入圖片描述
虛擬地址被分爲 5 個部分,前 4 個表項用於選擇頁,而最後一個索引表示頁內偏移。
大頁
常見的大小有 2MB 和 1GB。大頁通常用在使用大量內存的進程上,比如 Oracle、DPDK 等。
內存分佈
在這裏插入圖片描述
32位系統的內存分佈如上圖
從下到上分別分爲:
1 只讀段,包括代碼和常量等。
2 數據段,包括全局變量等。
3 堆,包括動態分配的內存,從低地址開始向上增長。
4 文件映射段,包括動態庫、共享內存等,從高地址開始向下增長。
5 棧,包括局部變量和函數調用的上下文等。棧的大小是固定的,一般是 8 MB。
其中呢,需要自己管理內存的語言,比如C/C++使用內存分配函數分配的都是堆和文件映射的空間,需要程序員自己管理申請和回收,不回收一直申請就產生了內存泄漏。
內存分配和回收
兩種分配方式
1 brk 當使用內存小於128K的時候,通過移動堆頂的位置來分配內存,回收並不立即還給系統,緩存起來,下次在用,提高效率
優點: 可以減少缺頁異常的發生,提高內存訪問效率
缺點: 內存工作繁忙時,頻繁的內存分配和釋放會造成內存碎片
2 mmap 大塊內存(大於 128K),則直接使用內存映射 mmap() 來分配,也就是在文件映射段找一塊空閒內存分配出去。
優點: 釋放時直接歸還系統,系統可以利用夥伴系統,減少內存碎片的數量
缺點:每次都會發生缺頁異常。在內存工作繁忙時,頻繁的內存分配會導致大量的缺頁異常,使內核的管理負擔增大。
更小的內存,比如1K,直接也分配單獨的頁,那就太浪費內存了。所以使用brk緩存起來,重複利用,在內核空間則使用slab管理小內存。
系統發現內存緊張的時候就會用下面三種方式回收內存:
1 回收緩存,比如使用 LRU(Least Recently Used)算法,回收最近使用最少的內存頁面;
2 回收不常訪問的內存,把不常用的內存通過交換分區直接寫到磁盤中;(SWAP)
3 殺死進程,內存緊張時系統還會通過 OOM(Out of Memory),直接殺掉佔用大量內存的進程。
其中,第三種,OOM是保護內存的一種方式,系統監控內存的使用情況,並根據oom_score 爲每個進程的內存使用情況進行評分:
1 一個進程消耗的內存越大,oom_score 就越大;
2 一個進程運行佔用的 CPU 越多,oom_score 就越小。
oom_score越大,越容易被系統殺死。
可以設置進程的 oom_adj ,從而調整進程的 oom_score。
oom_adj 的範圍是 [-17, 15],數值越大,表示進程越容易被 OOM 殺死;數值越小,表示進程越不容易被 OOM 殺死,其中 -17 表示禁止 OOM。
比如:
echo -16 > /proc/$(pidof sshd)/oom_adj
如何查看內存使用情況
free工具
$ free
total used free shared buff/cache available
Mem: 8169348 263524 6875352 668 1030472 7611064
Swap: 0 0 0
詳細解釋:
第一列,total 是總內存大小;
第二列,used 是已使用內存的大小,包含了共享內存;
第三列,free 是未使用內存的大小;
第四列,shared 是共享內存的大小;
第五列,buff/cache 是緩存和緩衝區的大小;
最後一列,available 是新進程可用內存的大小。
available 不僅包含未使用內存,還包括了可回收的緩存,所以一般會比未使用內存更大
top 工具
$ top

KiB Mem : 8169348 total, 6871440 free, 267096 used, 1030812 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 7607492 avail Mem

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
430 root 19 -1 122360 35588 23748 S 0.0 0.4 0:32.17 systemd-journal
1075 root 20 0 771860 22744 11368 S 0.0 0.3 0:38.89 snapd
1048 root 20 0 170904 17292 9488 S 0.0 0.2 0:00.24 networkd-dispat
1 root 20 0 78020 9156 6644 S 0.0 0.1 0:22.92 systemd
12376 azure 20 0 76632 7456 6420 S 0.0 0.1 0:00.01 systemd
12374 root 20 0 107984 7312 6304 S 0.0 0.1 0:00.00 sshd

VIRT 是進程虛擬內存的大小,只要是進程申請過的內存,即便還沒有真正分配物理內存,也會計算在內。
RES 是常駐內存的大小,也就是進程實際使用的物理內存大小,但不包括 Swap 和共享內存。SHR 是共享內存的大小,比如與其他進程共同使用的共享內存、加載的動態鏈接庫以及程序的代碼段等。
%MEM 是進程使用物理內存佔系統總內存的百分比。
第一,虛擬內存通常並不會全部分配物理內存。從上面的輸出,你可以發現每個進程的虛擬內存都比常駐內存大得多。
第二,共享內存 SHR 並不一定是共享的,比方說,程序的代碼段、非共享的動態鏈接庫,也都算在 SHR 裏。當然,SHR 也包括了進程間真正共享的內存。所以在計算多個進程的內存使用時,不要把所有進程的 SHR 直接相加得出結果。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章