前言
想必在linux上寫程序式的同學都有分析行程占用多少記憶體的經歷,或者被問到這樣的問題——你的程式在運行時占用了多少記憶體(物理記憶體)?
通常我們可以通過top命令查看行程占用了多少記憶體,這里我們可以看到VIRT、RES和SHR三個重要的指標,他們分別代表什么意思呢?
這是本文需要跟大家一起探討的問題,當然如果更加深入一點,你可能會問行程所占用的那些物理記憶體都用在了哪些地方?這時候top命令可能不能給到你你所想要的答案了,不過我們可以分析proc檔案系統提供的smaps檔案,這篇文章詳盡地列出了當前行程所占用物理記憶體的使用情況,
本文將分為三個部分:
1、簡要闡述虛擬記憶體和駐留記憶體這兩個重要的概念;
2、解釋top命令中VIRT、RES以及SHR三個引數的實際參考意義;
3、向大家介紹一下smaps檔案的格式,通過分析smaps檔案我們可以詳細了解行程物理記憶體的使用情況,比如mmap檔案占用了多少空間、動態記憶體開辟消耗了多少空間、函式呼叫堆疊消耗了多少空間等等,
一、關于記憶體的兩個概念
要理解top命令關于記憶體使用情況的輸出,我們必須首先搞清楚虛擬記憶體(Virtual Memory)和駐留記憶體(Resident Memory)兩個概念,
(1)虛擬記憶體
首先需要強調的是虛擬記憶體不同于物理記憶體,雖然兩者都包含記憶體字眼但是它們屬于兩個不同層面的概念,行程占用虛擬記憶體空間大并非意味著程式的物理記憶體也一定占用很大,虛擬記憶體是作業系統內核為了對行程地址空間進行管理(process address space management)而精心設計的一個邏輯意義上的記憶體空間概念,
我們程式中的指標其實都是這個虛擬記憶體空間中的地址,比如我們在寫完一段C++程式之后都需要采用g++進行編譯,這時候編譯器采用的地址其實就是虛擬記憶體空間的地址,因為這時候程式還沒有運行,何談物理記憶體空間地址?凡是程式運行程序中可能需要用到的指令或者資料都必須在虛擬記憶體空間中,
既然說虛擬記憶體是一個邏輯意義上(假象的)的記憶體空間,為了能夠讓程式在物理機器上運行,那么必須有一套機制可以讓這些假象的虛擬記憶體空間映射到物理記憶體空間(實實在在的RAM記憶體條上的空間),這其實就是作業系統中頁映射表(page table)所做的事情了,
內核會為系統中每一個行程維護一份相互獨立的頁映射表,頁映射表的基本原理是將程式運行程序中需要訪問的一段虛擬記憶體空間通過頁映射表映射到一段物理記憶體空間上,這樣CPU訪問對應虛擬記憶體地址的時候就可以通過這種查找頁映射表的機制訪問物理記憶體上的某個對應的地址,“頁(page)”是虛擬記憶體空間向物理記憶體空間映射的基本單元,
下圖演示了虛擬記憶體空間和物理記憶體空間的相互關系,它們通過Page Table關聯起來,其中虛擬記憶體空間中著色的部分分別被映射到物理記憶體空間對應相同著色的部分,而虛擬記憶體空間中灰色的部分表示在物理記憶體空間中沒有與之對應的部分,也就是說灰色部分沒有被映射到物理記憶體空間中,這么做也是本著“按需映射”的指導思想,因為虛擬記憶體空間很大,可能其中很多部分在一次程式運行程序中根本不需要訪問,所以也就沒有必要將虛擬記憶體空間中的這些部分映射到物理記憶體空間上,
虛擬記憶體空間到物理記憶體空間映射
到這里為止已經基本闡述了什么是虛擬記憶體了,
總結一下就是,虛擬記憶體是一個假象的記憶體空間,在程式運行程序中虛擬記憶體空間中需要被訪問的部分會被映射到物理記憶體空間中,虛擬記憶體空間大只能表示程式運行程序中可訪問的空間比較大,不代表物理記憶體空間占用也大,
(2)駐留記憶體
駐留記憶體,顧名思義是指那些被映射到行程虛擬記憶體空間的物理記憶體,上圖中,在系統物理記憶體空間中被著色的部分都是駐留記憶體,比如,A1、A2、A3和A4是行程A的駐留記憶體;B1、B2和B3是行程B的駐留記憶體,
行程的駐留記憶體就是行程實實在在占用的物理記憶體,一般我們所講的行程占用了多少記憶體,其實就是說的占用了多少駐留記憶體而不是多少虛擬記憶體,因為虛擬記憶體大并不意味著占用的物理記憶體大,
二、top命令中VIRT、RES和SHR
關于虛擬記憶體和駐留記憶體這兩個概念我們說到這里,下面一部分我們來看看top命令中VIRT、RES和SHR分別代表什么意思,
搞清楚了虛擬記憶體的概念之后解釋VIRT的含義就很簡單了,VIRT表示的是行程虛擬記憶體空間大小,對應到圖1中的行程A來說就是A1、A2、A3、A4以及灰色部分所有空間的總和,也就是說VIRT包含了在已經映射到物理記憶體空間的部分和尚未映射到物理記憶體空間的部分總和,
RES的含義是指行程虛擬記憶體空間中已經映射到物理記憶體空間的那部分的大小,對應到圖1中的行程A來說就是A1、A2、A3以及A4幾個部分空間的總和,所以說,看行程在運行程序中占用了多少記憶體應該看RES的值而不是VIRT的值,
最后來看看SHR所表示的含義,
SHR是share(共享)的縮寫,它表示的是行程占用的共享記憶體大小,在上圖中我們看到行程A虛擬記憶體空間中的A4和行程B虛擬記憶體空間中的B3都映射到了物理記憶體空間的A4/B3部分,咋一看很奇怪,
為什么會出現這樣的情況呢?
其實我們寫的程式會依賴于很多外部的動態庫(.so),比如libc.so、libld.so等等,這些動態庫在記憶體中僅僅會保存/映射一份,如果某個行程運行時需要這個動態庫,那么動態加載器會將這塊記憶體映射到對應行程的虛擬記憶體空間中,多個進展之間通過共享記憶體的方式相互通信也會出現這樣的情況,
這么一來,就會出現不同行程的虛擬記憶體空間會映射到相同的物理記憶體空間,這部分物理記憶體空間其實是被多個行程所共享的,所以我們將他們稱為共享記憶體,用SHR來表示,
某個行程占用的記憶體除了和別的行程共享的記憶體之外就是自己的獨占記憶體了,所以要計算行程獨占記憶體的大小只要用RES的值減去SHR值即可,
三、行程的smaps檔案
通過top命令我們已經能看出行程的虛擬空間大小(VIRT)、占用的物理記憶體(RES)以及和其他行程共享的記憶體(SHR),但是僅此而已,如果我想知道如下問題:
行程的虛擬記憶體空間的分布情況,比如heap占用了多少空間、檔案映射(mmap)占用了多少空間、stack占用了多少空間?
行程是否有被交換到swap空間的記憶體,如果有,被交換出去的大小?
mmap方式打開的資料檔案有多少頁在記憶體中是臟頁(dirty page)沒有被寫回到磁盤的?
mmap方式打開的資料檔案當前有多少頁面已經在記憶體中,有多少頁面還在磁盤中沒有加載到page cahe中?
以上這些問題都無法通過top命令給出答案,但是有時候這些問題正是我們在對程式進行性能瓶頸分析和優化時所需要回答的問題,所幸的是,世界上解決問題的方法總比問題本身要多得多,linux通過proc檔案系統為每個行程都提供了一個smaps檔案,通過分析該檔案我們就可以一一回答以上提出的問題,
在smaps檔案中,每一條記錄(如下圖所示)表示行程虛擬記憶體空間中一塊連續的區域,其中第一行從左到右依次表示地址范圍、權限標識、映射檔案偏移、設備號、inode、檔案路徑,詳細解釋可以參見
understanding-linux-proc-id-maps,
接下來8個欄位的含義分別如下:
? Size:表示該映射區域在虛擬記憶體空間中的大小,
? Rss:表示該映射區域當前在物理記憶體中占用了多少空間
? Shared_Clean:和其他行程共享的未被改寫的page的大小
? Shared_Dirty:和其他行程共享的被改寫的page的大小
? Private_Clean:未被改寫的私有頁面的大小,
? Private_Dirty:已被改寫的私有頁面的大小,
? Swap:表示非mmap記憶體(也叫anonymous memory,比如malloc動態分配出來的記憶體)由于物理記憶體不足被swap到交換空間的大小,
? Pss:該虛擬記憶體區域平攤計算后使用的物理記憶體大小(有些記憶體會和其他行程共享,例如mmap進來的),比如該區域所映射的物理記憶體部分同時也被另一個行程映射了,且該部分物理記憶體的大小為1000KB,那么該行程分攤其中一半的記憶體,即Pss=500KB,
smaps檔案中的一條記錄
有了smap如此詳細關于虛擬記憶體空間到物理記憶體空間的映射資訊,相信大家已經能夠通過分析該檔案回答上面提出的4個問題,
最后希望大家能夠通過閱讀本文對行程的虛擬記憶體和物理記憶體有一個更加清晰認識,并能更加準確理解top命令關于記憶體的輸出,最后可以通過smaps檔案更進一步分析行程使用記憶體的情況,
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/508032.html
標籤:Linux
上一篇:Linux云主機安全入侵排查步驟
下一篇:Nginx幾種負載均衡方式介紹
