你經常會在基於Linux的系統上面臨的問題之一是管理內存預算。如果程序使用的內存多於可用內存,則可能會發生交換,這常常會對性能造成嚴重影響,或者激活了內存溢出(OOM),從而完全殺死了進程。
在調整內存使用之前,通過配置優化或者負載管理,有助於了解給定程序實際使用多少內存。
如果你的系統本質上運行單用戶程序(總是有很多系統進程),那么這很容易。例如,如果我在具有128GB RAM的系統上運行專用的MySQL服務器,則可以使用“ used”作為已使用內存的標識,並使用“ available”作為仍可以使用的內存。
root@rocky:/mnt/data2/mysql# free -h total used free shared buff/cache available Mem: 125Gi 88Gi 5.2Gi 2.0Mi 32Gi 36Gi Swap: 63Gi 33Mi 63Gi
對於交換(swap),如果系統交換不頻繁,即使使用了交換空間,它通常也會保留“不需要的垃圾”,這不需要考慮計算。
如果你使用的是Percona的監控和管理工具(PMM),則會在“Memory Utilization”中看到它:
並在“node summary”面板的的“swap activity”圖中:
如果運行的多個進程共享着資源,則事情會變得復雜,因為“used”內存和進程之間沒有一一對應的映射。
讓我們只列出其中的一些復雜性:
·fork進程時的copy-on-write語義:所有的進程共享“used”部分的內存,直到進程修改本身修改數據,這時候進程有自己的內存拷貝
·共享內存:顧名思義,共享內存就是不同進程間共享的內存
·共享庫:庫被映射到每個使用它的進程,是進程使用的內存的一部分,盡管在進程間是共享相同的庫
·內存映射文件和匿名mmap():這里有很多更復雜的細節。例如,查看“內存映射文件”以獲取更多詳細信息。
考慮到這種復雜性,讓我們看一下“top”輸出,這是查看Linux當前負載的最常用程序之一。默認情況下,"top"按CPU使用率對進程進行排序,因此我們將按“ Shift-M”將其按(駐留)內存使用率進行排序。
首先你會注意到的是,該系統只有1GB的物理內存,並且具有多個進程,這些進程的虛擬內存(VIRT)超過1GB。
由於各種原因,現代內存分配器和編程語言(如GoLang)可以分配很多它們實際上沒有使用的虛擬內存,因此虛擬內存的使用對於了解一個進程需要多少實際內存沒有什么價值。
現在有常駐內存(RES),它向我們顯示了該進程實際使用了多少物理內存。這很好...但是有問題。內存可以是非駐留內存,這是因為它不是真正的“used”並且僅作為虛擬內存存在,或者是因為它已被換出。
如果我們查看統計內核實際為該進程提供的內容,就會發現有更多可用數據:
root@PMM2Server:~# cat /proc/3767/status Name: prometheus Umask: 0022 State: S (sleeping) Tgid: 3767 Ngid: 0 Pid: 3767 PPid: 3698 TracerPid: 0 Uid: 1000 1000 1000 1000 Gid: 1000 1000 1000 1000 FDSize: 256 Groups: 1000 NStgid: 3767 17 NSpid: 3767 17 NSpgid: 3767 17 NSsid: 3698 1 VmPeak: 3111416 kB VmSize: 3111416 kB VmLck: 0 kB VmPin: 0 kB VmHWM: 608596 kB VmRSS: 291356 kB RssAnon: 287336 kB RssFile: 4020 kB RssShmem: 0 kB VmData: 1759440 kB VmStk: 132 kB VmExe: 26112 kB VmLib: 8 kB VmPTE: 3884 kB VmSwap: 743116 kB HugetlbPages: 0 kB CoreDumping: 0 Threads: 11 SigQ: 0/3695 SigPnd: 0000000000000000 ShdPnd: 0000000000000000 SigBlk: fffffffe3bfa3a00 SigIgn: 0000000000000000 SigCgt: fffffffe7fc1feff CapInh: 00000000a80425fb CapPrm: 0000000000000000 CapEff: 0000000000000000 CapBnd: 00000000a80425fb CapAmb: 0000000000000000 NoNewPrivs: 0 Seccomp: 2 Speculation_Store_Bypass: vulnerable Cpus_allowed: 1 Cpus_allowed_list: 0 Mems_allowed: 00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001 Mems_allowed_list: 0 voluntary_ctxt_switches: 346 nonvoluntary_ctxt_switches: 545
VmSwap是一個特別有趣的數據點,因為它顯示了該進程使用的交換出的內存量。
VmRSS + VmSwap可以更好地指示進程所需的“物理”內存。在上面的例子中,它是1010MB,比常駐集284MB高很多,但比3038MB的“虛擬內存”大小小很多。
換出部分的問題,盡管我們不知道它是否被永久替換掉。例如,某些代碼或數據在你的程序中沒有用到,或者由於內存壓力被換出。我們確實需要將其放在實際內存(RAM)中以實現最佳性能-但我們沒有足夠的可用內存。
在本例子中,要查看的有用的數據點是主要頁面錯誤(major page faults)。它不在輸出的內容中,但是可以從另一個文件中看:/proc/[pid]/stat。這里是stack overflow上的一些有用的信息(https://stackoverflow.com/questions/39066998/what-are-the-meaning-of-values-at-proc-pid-stat)。
主要頁面錯誤(major page faults)數量多表示程序主要需要的不在物理內存中。這樣就會包含交換活動,還包括對當前不在RAM中,共享庫中當前未映射的代碼的引用、或對內存映射文件中的數據的引用。無論如何,主要頁面錯誤(major page faults)高發生率通常表明RAM壓力大。
不過,讓我們轉到“top”的輸出結果,看看是否可以在其中顯示更多有用的信息。您可以使用“ F”鍵盤快捷鍵來選擇要顯示的字段。
你可以添加SWAP,Major Faults Delta和used列來顯示我們在上面的談論的所有內容!
觀看這張圖片,我們可以看到很大一部分“ prometheus”進程被換出了,並且每秒發生2K個主要頁面錯誤。
“ clickhouse-serv”進程是另一個有趣的示例,因為它具有超過4G的“resident size”,但使用的內存相對較少,而主要頁面錯誤也較少。
最后,讓我們看一下“ percona-qan-api”進程,該進程只交換了很小的一部分,但也顯示了2K的主要頁面錯誤。老實說,我不太確定它是什么,但似乎與swap-IO沒有關系。
最后
是否想查看進程正在使用多少內存? 不要查看虛擬內存大小或常駐內存大小,而是查看定義為常駐內存大小+交換使用情況的“used”內存。
想看看是否有實際的內存壓力?針對你要調查的進程,查看系統級別(system-wide)的交換輸入/輸出統計信息以及主要頁面錯誤(major page faults)。
原文:https://www.percona.com/blog/2020/09/11/how-much-memory-does-the-process-really-take-on-linux/