一條報警引起的對 swap 認識
- 2020 年 3 月 10 日
- 筆記
一個伺服器報警資訊
今天講一個排查問題的小故事吧.
早上匆匆忙忙去上班了, 在一個例行的早會上, 被告知昨天 消息推送服務 記憶體報警超出了80%.

聽完後不由的虎軀一震, 因為從業務上和日常報警上是沒有回饋的, 機器報警目前只報告給特定的運維團隊, 並且距離此事情已經過去一段時間了.
抱著僥倖的心裡, 登錄到報警的機器上查看相關指標
首先查看了下負載、業務進程和相關日誌, 都沒有錯誤日誌記錄, 也沒有 OOM 被 kill 的記錄.
既然是記憶體報警, 先重點看下記憶體佔用情況
free -m

可以看到當前空餘記憶體還是挺多的, 但是 Swap used保持在 149M 的佔用, 證明了極有可能出現過機器記憶體不夠使用的情況, 由於沒有耗盡所有物理記憶體, 並沒有出現 OOM 情況, 具體原因先不探究, 我們看下為什麼會存在 swap 佔用.
接下來引入我們今天故事的主題 Swap
思考一個問題, 當作業系統記憶體不夠用的時會怎麼辦呢?
我們知道磁碟一般比記憶體是廉價的, 並且存儲容量大. 最好的辦法是將一部分當前不重要的數據放入磁碟來替代記憶體, 解燃眉之急.
其實,早期記憶體一般都比較小,很容易就出現記憶體不足的問題,所以很早就提出了一個交換分區(swap partition)的概念。
swap 分區是將磁碟當作記憶體使用,使得虛擬地址空間的範圍大小可以超出物理記憶體的實際大小,在物理記憶體空間不足時,可以將物理記憶體中的一些不重要數據拷貝到磁碟的 swap 分區中,從而讓出記憶體空間,並且在需要那些已被拷出數據時再從 swap 分區中拷回到記憶體,從而不再那麼容易發生OOM錯誤。
只有在出現物理記憶體耗盡或即將耗盡的時候,如果進程繼續請求分配記憶體,將報錯 out-of-memory(OOM)表示記憶體不足,並且在出現 OOM 的時候,作業系統將觸發 OOM Killer 程式從進程列表中篩選出一個記憶體密集型進程殺掉,從而釋放大片記憶體
介紹完 swap 概念, 我們看下指定進程佔用 swap 情況
#找到進程ID編號為 32132 cat /proc/32132/status 或 cat /proc/32132/smaps

我們核心的幾個參數含義
VmPeak 進程所使用的虛擬記憶體的峰值 VmSize 進程當前使用的虛擬記憶體的大小 VmLck 已經鎖住的物理記憶體的大小(鎖住的物理記憶體不能交換到硬碟) VmHWM 進程所使用的物理記憶體的峰值 VmRSS 進程當前使用的物理記憶體的大小 VmData 進程佔用的數據段大小 VmStk 進程佔用的棧大小 VmExe 進程佔用的程式碼段大小(不包括庫) VmLib 進程所載入的動態庫所佔用的記憶體大小(可能與其它進程共享) VmPTE 進程佔用的頁表大小(交換表項數量) VmSwap 進程所使用的交換區的大小
我們可以看到此進程佔用了 swap 交換區的 236kb
在上面參數含義中進程有虛擬記憶體、物理記憶體概念, 不知道你注意觀察過沒有, Top命令里也有 VIRT、RES、SHR相關的參數, 這是什麼意思呢?

VIRT
1、進程「需要的」虛擬記憶體大小,包括進程使用的庫、程式碼、數據,以及malloc、new分配的堆空間和分配的棧空間等;
2、假如進程新申請10MB的記憶體,但實際只使用了1MB,那麼它會增長10MB,而不是實際的1MB使用量。
3、VIRT = SWAP + RES
RES
1、進程當前使用的記憶體大小,包括使用中的malloc、new分配的堆空間和分配的棧空間,但不包括swap out量;
2、包含其他進程的共享;
3、如果申請10MB的記憶體,實際使用1MB,它只增長1MB,與VIRT相反;
4、關於庫佔用記憶體的情況,它只統計載入的庫文件所佔記憶體大小。
5、RES = CODE + DATA
SHR:
1、除了自身進程的共享記憶體,也包括其他進程的共享記憶體;
2、雖然進程只使用了幾個共享庫的函數,但它包含了整個共享庫的大小;
3、計算某個進程所佔的物理記憶體大小公式:RES – SHR;
4、swap out後,它將會降下來。
堆、棧分配的記憶體,如果沒有使用是不會佔用實存的,只會記錄到虛存。如果程式佔用實存比較多,說明程式申請記憶體多,實際使用的空間也多。如果程式佔用虛存比較多,說明程式申請來很多空間,但是沒有使用。工作中,遇到過有的程式虛存300G+, 實存只有不到15G。
了解了這些記憶體的概念後我們在重新回到 swap 上面來, 上面我們通過指定 pid 可以查看單個進程的 swap 佔用情況, 我可以清理或者重啟進程清理掉 swap 佔用, 但是我如何快速列出究竟是哪些進程 swap 佔用比較高呢.
查看佔用 swap 排名前10的進程pid
for i in $(cd /proc;ls | grep "^[0-9]"|awk '$0 >100');do awk '/Swap:/{a=a+$2}END{print "'$i'", a/1024"M"}' /proc/$i/smaps 2>/dev/null;done | sort -k2nr | head -10

經過重啟清理後, 重新觀察 swap 佔用情況, 釋放了部分空間

關於 swap 其他命令
開啟swap
# 含義為增加1G的交換分區, 文件大小= bs * count dd if=/dev/zero of=/root/swapfile bs=1M count=1024 mkswap /root/swapfile #建立swap的文件系統 swapon /root/swapfile #啟用swap文件
查看 swap 使用情況
swapon -s 或 cat /proc/swaps

#關閉交換區 swapoff /root/swapfile #查看記憶體情況 free -m

我們發現 Swap 項全部變為了0, swapoff 可以不用重啟進程快速釋放交互區數據, 但存在的風險是數據是有可能丟失的.
#重新開啟 swapon /root/swapfile #查看記憶體情況 free -m

參考文章
- https://blog.csdn.net/u011296165/article/details/95066920
- https://www.cnblogs.com/xudong-bupt/p/8643094.html
- https://www.cnblogs.com/liujunjun/p/12404588.html
- https://www.cnblogs.com/kerrycode/p/5246383.html