伺服器效能指標(三)——記憶體使用分析及問題排查

HollisChuang發表於2019-03-04

平常的工作中,在衡量伺服器的效能時,經常會涉及到幾個指標,load、cpu、mem、qps、rt等。每個指標都有其獨特的意義,很多時候線上上出現問題時,往往會伴隨著某些指標的異常。大部分情況下,在問題發生之前,某些指標就會提前有異常顯示。

第一篇文章中,我們介紹了一個重要的指標就是負載(Load),其中我們提到Linux的負載高,主要是由於CPU使用、記憶體使用、IO消耗三部分構成。任意一項使用過多,都將導致伺服器負載的急劇攀升。本文就來分析其中的第二項,記憶體使用。

什麼是記憶體

記憶體是計算機中重要的部件之一,它是與CPU進行溝通的橋樑。計算機中所有程式的執行都是在記憶體中進行的,因此記憶體的效能對計算機的影響非常大。

記憶體(Memory)也被稱為記憶體儲器,其作用是用於暫時存放CPU中的運算資料,以及與硬碟等外部儲存器交換的資料。

實體記憶體

實體記憶體指通過實體記憶體條而獲得的記憶體空間。即隨機存取儲存器(random access memory,RAM),是與CPU直接交換資料的內部儲存器,也叫主存(記憶體)。

虛擬記憶體

虛擬記憶體是計算機系統記憶體管理的一種技術。它使得應用程式認為它擁有連續可用的記憶體(一個連續完整的地址空間),而實際上,它通常是被分隔成多個實體記憶體碎片,還有部分暫時儲存在外部磁碟儲存器上,在需要時進行資料交換(也就是說,當實體記憶體不足時,可能會借用硬碟空間來充當記憶體使用)。與沒有使用虛擬記憶體技術的系統相比,使用這種技術的系統使得大型程式的編寫變得更容易,對真正的實體記憶體(例如RAM)的使用也更有效率。

Swap分割槽

Swap分割槽(即交換區)在系統的實體記憶體不夠用的時候,把硬碟空間中的一部分空間釋放出來,以供當前執行的程式使用。那些被釋放的空間可能來自一些很長時間沒有什麼操作的程式,這些被釋放的空間被臨時儲存到Swap分割槽中,等到那些程式要執行時,再從Swap分割槽中恢復儲存的資料到記憶體中。

程式執行時的資料載入,執行緒併發,I/O緩衝等等,都依賴於記憶體,可用記憶體的大小,決定了程式是否能正常執行以及執行的效能。

檢視記憶體使用情況

在Linux機器上,有多個命令都可以檢視機器的記憶體資訊。其中包括free、top等。

free命令

free命令可以顯示Linux系統中空閒的、已用的實體記憶體swap分割槽以及被核心緩衝區記憶體。在Linux系統監控的工具中,free命令是最經常使用的命令之一。

$free
             total       used       free     shared    buffers     cached
Mem:       8388608    2926968    5461640          0          0    1654392
-/+ buffers/cache:    1272576    7116032
Swap:     16777208          0   16777208
複製程式碼

上圖中,一共有3行6列資料,行資料的意義如下: Mem 行是記憶體的使用情況。 -/+ buffers/cache 行是實體記憶體的快取統計情況。 Swap 行是交換空間的使用情況。

前面分別介紹過了實體記憶體和Swap分割槽。這裡再介紹一下buffers和cache。

buffer與cache的區別

A buffer is something that has yet to be "written" to disk.

A cache is something that has been "read" from the disk and stored for later use.

簡單點說:

buffers 就是存放要輸出到disk(塊裝置)的資料,緩衝滿了一次寫,提高IO效能(記憶體 -> 磁碟)

cached 就是存放從disk上讀出的資料,常用的快取起來,減少IO(磁碟 -> 記憶體)

buffer 和 cache,兩者都是RAM中的資料。簡單來說,buffer是即將要被寫入磁碟的,cache是被從磁碟中讀出來的。

介紹完了buffer和cache的區別,接下來分析下free命令查詢到的資料。

Mem行

             total       used       free     shared    buffers     cached
Mem:       8388608    2926968    5461640          0          0    1654392
複製程式碼

這一行展示實體記憶體的整體情況。

Total:8388608。表示實體記憶體總大小。

Used :2926968。表示總計分配給快取(包含buffers 與cache )使用的數量,但其中可能部分快取並未實際使用。

Free :5461640。表示未被分配的記憶體。

Shared:0。共享記憶體,一般系統不會用到。

Buffers:0。系統分配但未被使用的buffers 數量。

Cached:1654392。系統分配但未被使用的cache 數量。

total(Mem) = used(Mem) + free(Mem)

-/+ buffers/cache 行

             total       used       free     shared    buffers     cached
-/+ buffers/cache:    1272576    7116032
複製程式碼

Used:1272576。 表示實際使用的buffers 與cache 總量,也是實際使用的記憶體總量。

Free:7116032。 未被使用的buffers 與cache 和未被分配的記憶體之和,這就是系統當前實際可用記憶體。

used(-/+ buffers/cache) = used(Mem) - cached(Mem) - buffers(Mem)

free(-/+ buffers/cache) = free(Mem) + cached (Mem)+ buffers(Mem)

Swap 行

$free
             total       used       free     shared    buffers     cached
Swap:     16777208          0   16777208
複製程式碼

Total:16777208。Swap記憶體總大小。

Used:0。表示已分配的Swap大小。

Free:16777208。表示未被分配的記憶體。

接下來,再來整體看一下資料

$free
             total       used       free     shared    buffers     cached
Mem:       8388608    2926968    5461640          0          0    1654392
-/+ buffers/cache:    1272576    7116032
Swap:     16777208          0   16777208
複製程式碼

機器上實際可用記憶體大小:

   Free(-/+ buffers/cache)= Free(Mem)+buffers(Mem)+Cached(Mem);

                               7116032 = 5461640 + 0+ 1654392
複製程式碼

已經分配的記憶體大小:

   Used(Mem) = Used(-/+ buffers/cache)+ buffers(Mem) + Cached(Mem)

          2926968 = 1272576 + 0 + 1654392
複製程式碼

實體記憶體總大小

   total(Mem) = used(-/+ buffers/cache) + free(-/+ buffers/cache)

                 8388608 = 1272576 + 7116032
複製程式碼

總結一下,整個機器的總記憶體大小8388608,其中已經分配的記憶體有2926968,還未分配的記憶體有5461640。而分配的2926968中,有1654392還沒有使用,有1272576已經用掉了。當前機器中還有7116032記憶體可以使用。

free命令引數

-m 以M為單位顯示記憶體

$free -m
             total       used       free     shared    buffers     cached
Mem:          8192       2802       5389          0          0       1559
-/+ buffers/cache:       1243       6948
Swap:        16383          0      16383
複製程式碼

-g 以G為單位顯示記憶體

$free -g
             total       used       free     shared    buffers     cached
Mem:          8          2          5         0         0           1
-/+ buffers/cache:       1          6
Swap:        16          0          16
複製程式碼

-s 2持續的觀察記憶體的狀況,每隔2秒列印一次

$free -s 2
         total       used       free     shared    buffers     cached
Mem:       8388608    2873128    5515480          0          0    1600588
-/+ buffers/cache:    1272540    7116068
Swap:     16777208          0   16777208

             total       used       free     shared    buffers     cached
Mem:       8388608    2873168    5515440          0          0    1600628
-/+ buffers/cache:    1272540    7116068
Swap:     16777208          0   16777208
複製程式碼

除了free ,還可以在Linux下可以使用/proc/meminfo檔案檢視作業系統記憶體的使用狀態,其實,free命令的內容也是來自於/proc/meminfo檔案。

top命令

top命令是Linux下常用的效能分析工具,能夠實時顯示系統中各個程式的資源佔用狀況,類似於Windows的工作管理員。

在前面兩篇文章中介紹過使用top命令檢視Load Avg和CPU利用率。top還會列印的一部分資訊就是記憶體情況。

top - 17:49:32 up 2 days,  6:25,  1 user,  load average: 0.01, 0.09, 0.12
Tasks:  30 total,   1 running,  29 sleeping,   0 stopped,   0 zombie
Cpu(s):  0.1%us,  0.0%sy,  0.0%ni, 88.0%id,  3.8%wa,  0.0%hi,  0.0%si,  8.1%st
Mem:   8388608k total,  2884716k used,  5503892k free,        0k buffers
Swap: 16777208k total,        0k used, 16777208k free,  1612080k cached

   PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
 85690 admin     20   0 5138m 1.1g  47m S  2.3 13.9  93:28.92 java
複製程式碼

上面的Mem行和Swap行展示的就是記憶體的使用情況。並且也會按照進行展示不同程式的記憶體佔用情況。十分好用。

Java Web應用記憶體佔用飆高排查思路

JVM以一個程式(Process)的身份執行在Linux系統上,對於Linux來說,JVM不過是一個具有自助管理記憶體的乖孩子而已。

一般在應用啟動時都可以通過JVM引數來設定JVM記憶體的大小。如果超過這個限制就會丟擲異常。所以,我們比較常見的記憶體佔用過高問題,最顯著的現象就是丟擲各種OutOfMemoryError。

有一種可能導致直接記憶體,也就是Linux的實體記憶體過高的情況,就是NIO的使用。NIO引入了一種基於通道與緩衝區的IO方式,他可以使用Native函式庫直接分配堆外記憶體,然後通過一個儲存在Java堆中的DirectByteBuffer物件作為這塊記憶體的引用進行操作。

所以,在使用NIO的時候,要特別小心,避免導致機器記憶體被擠滿。

導致JVM中記憶體佔用飆高的原因可能有很多。最常見的就是記憶體洩露。

記憶體洩露排查思路

1、使用top命令,檢視佔用記憶體較高的程式ID。

➜  ~ top

PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
3331 admin     20   0 7127m 2.6g  38m S 10.7 90.6  10:20.26 java
複製程式碼

發現PID為3331的程式佔用記憶體 90.6%。而且是一個Java程式,基本斷定是程式問題。

2、使用jmap檢視記憶體情況,並分析是否存在記憶體洩露。

jmap -heap 3331:檢視java 堆(heap)使用情況

jmap -histo 3331:檢視堆記憶體(histogram)中的物件數量及大小

jmap -histo:live 3331:JVM會先觸發gc,然後再統計資訊

jmap -dump:format=b,file=heapDump 3331:將記憶體使用的詳細情況輸出到檔案
複製程式碼

得到堆dump檔案後,可以進行物件分析。如果有大量物件在持續被引用,並沒有被釋放掉,那就產生了記憶體洩露,就要結合程式碼,把不用的物件釋放掉。

wechat

參考資料

Linux 記憶體 buffer 和 cache 的區別

相關文章