平常的工作中,在衡量伺服器的效能時,經常會涉及到幾個指標,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檔案後,可以進行物件分析。如果有大量物件在持續被引用,並沒有被釋放掉,那就產生了記憶體洩露,就要結合程式碼,把不用的物件釋放掉。