最近在數據庫上執行SQL時,發現該SQL卡住了,於是top看了下該進程cpu、內存的使用情況,然后發現top列出的3個指標VIRT、RES、SHR,我對這三者不太了解,於是就查了下,發現這篇文章講的不錯,從中提取了一部分內容,作為筆記。
首先,需要了解兩個重要的概念,虛擬內存(Virtual Memory)和駐留內存(Resident Memory)
虛擬內存
需要強調的是虛擬內存不同於物理內存,雖然兩者都包含內存字眼但是他們屬於兩個不同層面的概念。進程占用虛擬內存空間大並非意味着程序的物理內存也一定占用很大。虛擬內存是操作系統內核為了對進程地址空間進行管理啊(process address space management)而精心設計的一個邏輯意義上的內存空間概念。我們程序中的指針其實都是這個虛擬內存空間的地址。我們程序中的指針其實都是這個虛擬內存空間中的地址。比如我們在寫完一段C++程序之后都需要采用g++進行編譯,這時候編譯器采用的地址其實就是虛擬內存空間的地址。因為這時候程序還沒有運行,所以就沒有所謂的物理內存空間地址。凡是程序運行過程中可能需要用到的指令或者數據都必須在虛擬內存空間中。既然說虛擬內存是一個邏輯意義上(假象的)內存空間,為了能夠讓程序在物理機器上運行,那么必須統一有一套機制可以讓這些假象的虛擬呢村空間映射到屋里內存空間(即RAM內存條上的空間)。這其實就是操作系統中也映射表(page table)所做的事情了。內核會為系統中每一個進程維護一份相互獨立的頁映射表,頁映射表的基本原理是將程序運行過程中所需要訪問的一段虛擬內存空間通過頁映射表映射到一段屋里內存空間上,這樣cpu訪問虛擬內存地址的時候就可以通過這種查找頁映射表的機制訪問屋里內存上的某個對應的地址。“頁”是虛擬內存空間向物理內存空間映射的基本單元。
下圖1演示了虛擬空間和物理內存空間的相互關系,它們通過Page Table關聯起來。其中虛擬內存空間中着色的部分分別被映射到物理內存空間對應相同着色的部分。而虛擬內存空間中灰色的部分表示物理內存空間中沒有與之對應的部分,也就是說灰色部分沒有別映射到物理內存空間中。這么做也是本着“按需映射”的指導思想,因為虛擬內存空間很大,可能其中很多部分再一次程序運行過程中根本不需要訪問,所以也就沒必要將虛擬內存空間中的這些部分映射到物理內存空間上。
圖一:虛擬內存空間和物理內存空間的相互關系
到這里為止,已經基本闡述了什么事虛擬內存了。總結一下就是,虛擬內存是一個假象的內存空間,在程序運行過程中虛擬內存空間中需要被訪問的部分會被映射到物理內存中。虛擬內存空間大只能表示程序運行過重中可訪問的空間比較大,不能代表屋里內存空間占用也大。
駐留內存
駐留內存,顧名思義是指那些被映射到進程虛擬內存的物理內存。上圖1中,在系統物理內存空間中被着色的部分是駐留內存。比如,A1、A2、A3和A4都是A的駐留內存;B1、B2和B3是進程B的駐留內存。進程的主流內存就是進程實際占用的物理內存。一般我們所講的進程占用了多少內存,其實就是說的占用了多少駐留內存而不是多少虛擬內存。因此,虛擬內存大並不意味着占用的物理內存大。
top命令中VIRT、RES和SHR所別代表的含義
-
VIRT表示的是進程虛擬內存空間大小。對應到圖一種的進程A來說就是A1、A2、A3、A4以及灰色部分所有空間的綜合。也就是說VIRT包含了再已經映射到物理內存空間的部分和尚未映射到物理內存空間的部分的總和。
-
RES的含義是指進程虛擬內存空間中已經映射到屋里內存空間的那部分的餓大小。對應圖1中的進程A來說就是A1、A2、A3、A4以幾個部分空間的總和。所以說,看進程運行過程中占用了多少內存應該看RES的值而不是VIRT的值。
-
SHR是share的縮寫,它表示的是進程占用的共享內存大小。在上圖一種我們看到進程A虛擬內存空間中的A4和進程B虛擬內存空間中的B3都映射到物理內存空間的A4/B3部分。咋一看很奇怪,為什么會出現這樣的情況呢?其實我們寫的程序會依賴於很多外部的動態庫(.so),比如libc.so、libld.so等等。這些動態庫在內存中僅僅會保存/映射一份,如果某個進程運行時需要這個動態庫,那么動態加載器會將這塊內存映射到對應進程的虛擬內存空間中。多個進程之間通過共享內存的方式相互通信也會出現這樣的情況。這么一來,就會出現不同進程的虛擬內存空間會映射到相同物理內存空間。這部分物理內存空間其實是被多個進程鎖共享的,所以我們將他們成為共享內存,用SHR表示。某個進程占用的內存除了和別的進程共享的內存之外就是自己的獨占內存了。所以要計算進程斷站內存的逮蝦只要用RES的值減去SHR值即可,即RES = VIRT - SHR,如下圖二。
圖二