線上服務(wù)器內(nèi)存分析及問題排查
平常的工作中,在衡量服務(wù)器的性能時,經(jīng)常會涉及到幾個指標(biāo),load、cpu、mem、qps、rt等。每個指標(biāo)都有其獨特的意義,很多時候在線上出現(xiàn)問題時,往往會伴隨著某些指標(biāo)的異常。大部分情況下,在問題發(fā)生之前,某些指標(biāo)就會提前有異常顯示。
在第一篇文章中,我們介紹了一個重要的指標(biāo)就是負(fù)載(Load),其中我們提到Linux的負(fù)載高,主要是由于CPU使用、內(nèi)存使用、IO消耗三部分構(gòu)成。任意一項使用過多,都將導(dǎo)致服務(wù)器負(fù)載的急劇攀升。本文是該系列的第三篇,來分析一下影響機器負(fù)載的幾個原因中的第二項,內(nèi)存使用。
什么是內(nèi)存
內(nèi)存是計算機中重要的部件之一,它是與CPU進行溝通的橋梁。計算機中所有程序的運行都是在內(nèi)存中進行的,因此內(nèi)存的性能對計算機的影響非常大。
內(nèi)存(Memory)也被稱為內(nèi)存儲器,其作用是用于暫時存放CPU中的運算數(shù)據(jù),以及與硬盤等外部存儲器交換的數(shù)據(jù)。
物理內(nèi)存
物理內(nèi)存指通過物理內(nèi)存條而獲得的內(nèi)存空間。即隨機存取存儲器(random access memory,RAM),是與CPU直接交換數(shù)據(jù)的內(nèi)部存儲器,也叫主存(內(nèi)存)。
虛擬內(nèi)存
虛擬內(nèi)存是計算機系統(tǒng)內(nèi)存管理的一種技術(shù)。它使得應(yīng)用程序認(rèn)為它擁有連續(xù)可用的內(nèi)存(一個連續(xù)完整的地址空間),而實際上,它通常是被分隔成多個物理內(nèi)存碎片,還有部分暫時存儲在外部磁盤存儲器上,在需要時進行數(shù)據(jù)交換(也就是說,當(dāng)物理內(nèi)存不足時,可能會借用硬盤空間來充當(dāng)內(nèi)存使用)。與沒有使用虛擬內(nèi)存技術(shù)的系統(tǒng)相比,使用這種技術(shù)的系統(tǒng)使得大型程序的編寫變得更容易,對真正的物理內(nèi)存(例如RAM)的使用也更有效率。
Swap分區(qū)
Swap分區(qū)(即交換區(qū))在系統(tǒng)的物理內(nèi)存不夠用的時候,把硬盤空間中的一部分空間釋放出來,以供當(dāng)前運行的程序使用。那些被釋放的空間可能來自一些很長時間沒有什么操作的程序,這些被釋放的空間被臨時保存到Swap分區(qū)中,等到那些程序要運行時,再從Swap分區(qū)中恢復(fù)保存的數(shù)據(jù)到內(nèi)存中。
程序運行時的數(shù)據(jù)加載,線程并發(fā),I/O緩沖等等,都依賴于內(nèi)存,可用內(nèi)存的大小,決定了程序是否能正常運行以及運行的性能。
查看內(nèi)存使用情況
在Linux機器上,有多個命令都可以查看機器的內(nèi)存信息。其中包括free、top等。
free命令
free命令可以顯示Linux系統(tǒng)中空閑的、已用的物理內(nèi)存,swap分區(qū)以及被內(nèi)核緩沖區(qū)內(nèi)存。在Linux系統(tǒng)監(jiān)控的工具中,free命令是最經(jīng)常使用的命令之一。
- $free
- total used free shared buffers cached
- Mem: 8388608 2926968 5461640 0 0 1654392
- -/+ buffers/cache: 1272576 7116032
- Swap: 16777208 0 16777208
上圖中,一共有3行6列數(shù)據(jù),行數(shù)據(jù)的意義如下: Mem 行是內(nèi)存的使用情況。 -/+ buffers/cache 行是物理內(nèi)存的緩存統(tǒng)計情況。 Swap 行是交換空間的使用情況。
前面分別介紹過了物理內(nèi)存和Swap分區(qū)。這里再介紹一下buffers和cache。
buffer與cache的區(qū)別
- 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(塊設(shè)備)的數(shù)據(jù),緩沖滿了一次寫,提高IO性能(內(nèi)存 -> 磁盤)
cached 就是存放從disk上讀出的數(shù)據(jù),常用的緩存起來,減少IO(磁盤 -> 內(nèi)存)
buffer 和 cache,兩者都是RAM中的數(shù)據(jù)。簡單來說,buffer是即將要被寫入磁盤的,cache是被從磁盤中讀出來的。
介紹完了buffer和cache的區(qū)別,接下來分析下free命令查詢到的數(shù)據(jù)。
Mem行
- total used free shared buffers cached
- em: 8388608 2926968 5461640 0 0 1654392
這一行展示物理內(nèi)存的整體情況。
Total:8388608。表示物理內(nèi)存總大小。
Used :2926968。表示總計分配給緩存(包含buffers 與cache )使用的數(shù)量,但其中可能部分緩存并未實際使用。
Free :5461640。表示未被分配的內(nèi)存。
Shared:0。共享內(nèi)存,一般系統(tǒng)不會用到。
Buffers:0。系統(tǒng)分配但未被使用的buffers 數(shù)量。
Cached:1654392。系統(tǒng)分配但未被使用的cache 數(shù)量。
- total(Mem) = used(Mem) + free(Mem)
-/+ buffers/cache 行
- total used free shared buffers cached
- -/+ buffers/cache: 1272576 7116032
Used:1272576。 表示實際使用的buffers 與cache 總量,也是實際使用的內(nèi)存總量。
Free:7116032。 未被使用的buffers 與cache 和未被分配的內(nèi)存之和,這就是系統(tǒng)當(dāng)前實際可用內(nèi)存。
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內(nèi)存總大小。
Used:0。表示已分配的Swap大小。
Free:16777208。表示未被分配的內(nèi)存。
接下來,再來整體看一下數(shù)據(jù)。
- $free
- total used free shared buffers cached
- Mem: 8388608 2926968 5461640 0 0 1654392
- -/+ buffers/cache: 1272576 7116032
- Swap: 16777208 0 16777208
機器上實際可用內(nèi)存大?。?/p>
- Free(-/+ buffers/cache)= Free(Mem)+buffers(Mem)+Cached(Mem);
- 7116032 = 5461640 + 0+ 1654392
已經(jīng)分配的內(nèi)存大?。?/p>
- Used(Mem) = Used(-/+ buffers/cache)+ buffers(Mem) + Cached(Mem)
- 2926968 = 1272576 + 0 + 1654392
物理內(nèi)存總大小
- total(Mem) = used(-/+ buffers/cache) + free(-/+ buffers/cache)
- 8388608 = 1272576 + 7116032
總結(jié)一下,整個機器的總內(nèi)存大小8388608,其中已經(jīng)分配的內(nèi)存有2926968,還未分配的內(nèi)存有5461640。而分配的2926968中,有1654392還沒有使用,有1272576已經(jīng)用掉了。當(dāng)前機器中還有7116032內(nèi)存可以使用。
free命令參數(shù)
-m 以M為單位顯示內(nèi)存
- $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為單位顯示內(nèi)存
- $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持續(xù)的觀察內(nèi)存的狀況,每隔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文件查看操作系統(tǒng)內(nèi)存的使用狀態(tài),其實,free命令的內(nèi)容也是來自于/proc/meminfo文件。
top命令
top命令是Linux下常用的性能分析工具,能夠?qū)崟r顯示系統(tǒng)中各個進程的資源占用狀況,類似于Windows的任務(wù)管理器。
在前面兩篇文章中介紹過使用top命令查看Load Avg和CPU利用率。top還會打印的一部分信息就是內(nèi)存情況。
- 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行展示的就是內(nèi)存的使用情況。并且也會按照進行展示不同進程的內(nèi)存占用情況。十分好用。
Java Web應(yīng)用內(nèi)存占用飆高排查思路
JVM以一個進程(Process)的身份運行在Linux系統(tǒng)上,對于Linux來說,JVM不過是一個具有自助管理內(nèi)存的乖孩子而已。
一般在應(yīng)用啟動時都可以通過JVM參數(shù)來設(shè)置JVM內(nèi)存的大小。如果超過這個限制就會拋出異常。所以,我們比較常見的內(nèi)存占用過高問題,最顯著的現(xiàn)象就是拋出各種OutOfMemoryError。
有一種可能導(dǎo)致直接內(nèi)存,也就是Linux的物理內(nèi)存過高的情況,就是NIO的使用。NIO引入了一種基于通道與緩沖區(qū)的IO方式,他可以使用Native函數(shù)庫直接分配堆外內(nèi)存,然后通過一個存儲在Java堆中的DirectByteBuffer對象作為這塊內(nèi)存的引用進行操作。
所以,在使用NIO的時候,要特別小心,避免導(dǎo)致機器內(nèi)存被擠滿。
導(dǎo)致JVM中內(nèi)存占用飆高的原因可能有很多。最常見的就是內(nèi)存泄露。
內(nèi)存泄露排查思路
1、使用top命令,查看占用內(nèi)存較高的進程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
發(fā)現(xiàn)PID為3331的進程占用內(nèi)存 90.6%。而且是一個Java進程,基本斷定是程序問題。
2、使用jmap查看內(nèi)存情況,并分析是否存在內(nèi)存泄露。
- jmap -heap 3331:查看java 堆(heap)使用情況
- jmap -histo 3331:查看堆內(nèi)存(histogram)中的對象數(shù)量及大小
- jmap -histo:live 3331:JVM會先觸發(fā)gc,然后再統(tǒng)計信息
- jmap -dump:format=b,file=heapDump 3331:將內(nèi)存使用的詳細(xì)情況輸出到文件
得到堆dump文件后,可以進行對象分析。如果有大量對象在持續(xù)被引用,并沒有被釋放掉,那就產(chǎn)生了內(nèi)存泄露,就要結(jié)合代碼,把不用的對象釋放掉。
【本文是51CTO專欄作者Hollis的原創(chuàng)文章,作者微信公眾號Hollis(ID:hollischuang)】