什麼是記憶體
記憶體(Memory)是計算機的重要部件之一,也稱記憶體儲器和主儲存器,它用於暫時存放CPU中的運算資料,與硬碟等外部儲存器交換的資料。
它是外存與CPU進行溝通的橋樑,計算機中所有程式的執行都在記憶體中進行,記憶體效能的強弱影響計算機整體發揮的水平。
只要計算機開始執行,作業系統就會把需要運算的資料從記憶體調到CPU中進行運算,當運算完成,CPU將結果傳送出來。
記憶體的執行也決定計算機整體執行快慢的程度。
Linux記憶體回收機制
為啥要回收:
- 核心需要為任何時刻突發到來的記憶體申請提供足夠的記憶體,以便cache的使用和其他相關記憶體的使用不至於讓系統的剩餘記憶體長期處於很少的狀態。
- 當真的有大於空閒記憶體的申請到來的時候,會觸發強制記憶體回收。
記憶體回收針對的目標有兩種,一種是針對zone的,另一種是針對一個memcg的,把針對zone的記憶體回收方式分為三種,分別是快速記憶體回收、直接記憶體回收、kswapd記憶體回收。
檢視Linux記憶體情況
檢視/proc/meminfo
[root@test ~]# cat /proc/meminfo
MemTotal: 16166688 kB
MemFree: 14051412 kB
MemAvailable: 14772588 kB
Buffers: 2116 kB
Cached: 1073260 kB
SwapCached: 0 kB
Active: 770384 kB
Inactive: 698264 kB
Active(anon): 450156 kB
Inactive(anon): 76748 kB
Active(file): 320228 kB
Inactive(file): 621516 kB
Unevictable: 0 kB
Mlocked: 0 kB
SwapTotal: 33554428 kB
SwapFree: 33554428 kB
Dirty: 476 kB
Writeback: 0 kB
AnonPages: 393328 kB
Mapped: 153828 kB
Shmem: 133628 kB
Slab: 246448 kB
SReclaimable: 133892 kB
SUnreclaim: 112556 kB
KernelStack: 13472 kB
PageTables: 30496 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 41637772 kB
Committed_AS: 4257776 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 320696 kB
VmallocChunk: 34350426108 kB
HardwareCorrupted: 0 kB
AnonHugePages: 155648 kB
CmaTotal: 0 kB
CmaFree: 0 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
DirectMap4k: 279276 kB
DirectMap2M: 6965248 kB
DirectMap1G: 11534336 kB
使用free
命令檢視
[root@test ~]# free -h
total used free shared buff/cache available
Mem: 15G 874M 13G 130M 1.2G 14G
Swap: 31G 0B 31G
引數說明:
- total:總記憶體大小。
- used:已經使用的記憶體大小(這裡麵包含cached和buffers和shared部分)。
- free:空閒的記憶體大小。
- shared:程式間共享記憶體(一般不會用,可以忽略)。
- buffers:記憶體中寫完的東西快取起來,這樣快速響應請求,後面資料再定期刷到磁碟上。
- cached:記憶體中讀完快取起來內容佔的大小(這部分是為了下次查詢時快速返回)。
- available:還可以被應用程式使用的實體記憶體大小,和free的區別是,free是真正未被使用的記憶體,available是包括buffers、cached的。
- Swap:硬碟上交換分割槽的使用大小。
Buffer和Cache
Cache(快取),為了調高CPU和記憶體之間資料交換而設計,Buffer(緩衝)為了提高記憶體和硬碟(或其他I/O裝置的資料交換而設計)。
Cache主要是針對讀操作設計的,不過Cache概念可能容易混淆,我理解為CPU本身就有Cache,包括一級快取、二級快取、三級快取,我們知道CPU所有的指令操作對接的都是記憶體,而CPU的處理能力遠高於記憶體速度,所以為了不讓CPU資源閒置,Intel等公司在CPU內部整合了一些Cache,但畢竟不能放太多電路在裡面,所以這部分Cache並不是很大,主要是用來存放一些常用的指令和常用資料,真正大部分Cache的資料應該是佔用記憶體的空間來快取請求過的資料,即上面的Cached部分(這部分純屬個人理解,正確與否有待考證)。
Buffer主要是針對寫操作設計的,更細的說是針對記憶體和硬碟之間的寫操作來設計的,目的是將寫的操作集中起來進行,減少磁碟碎片和硬碟反覆定址過程,提高效能。
在Linux系統內部有一個守護程式會定期清空Buffer中的內容,將其寫入硬碟內,當手動執行sync命令時也會觸發上述操作。
Swap
雖然現在的記憶體已經變得非常廉價,但是swap仍然有很大的使用價值,合理的規劃和使用swap分割槽,對系統穩定執行至關重要。
Linux下可以使用檔案系統中的一個常規檔案或者一個獨立分割槽作為交換空間使用。同時linux允許使用多個交換分割槽或者交換檔案。
記憶體洩漏和記憶體溢位
記憶體溢位(OOM,out of memory),是指程式在申請記憶體時,沒有足夠的記憶體空間供其使用,出現out of memory;比如申請了一個integer,但給它存了long才能存下的數,那就是記憶體溢位。
記憶體洩露(memory leak),是指程式在申請記憶體後,無法釋放已申請的記憶體空間,一次記憶體洩露危害可以忽略,但記憶體洩露堆積後果很嚴重,無論多少記憶體,遲早會被佔光。
如何判斷記憶體洩露
用 jstat -gcutil PID
,觀察Old這個引數,如果每次執行完FULLGC之後Old區的值一次比一次升高,就可以判斷為發生了記憶體洩漏。
如何判斷記憶體溢位
Heap Dump(堆轉儲檔案)它是一個Java程式在某個時間點上的記憶體快照。Heap Dump是有著多種型別的。不過總體上heap dump在觸發快照的時候都儲存了java物件和類的資訊。通常在寫heap dump檔案前會觸發一次FullGC,所以heap dump檔案中儲存的是FullGC後留下的物件資訊。
通過設定如下的JVM引數,可以在發生OutOfMemoryError後獲取到一份HPROF二進位制Heap Dump檔案:
-XX:+HeapDumpOnOutOfMemoryError
生成的檔案會直接寫入到工作目錄。
注意:該方法需要JDK5以上版本。
轉存堆記憶體資訊後,需要對檔案進行分析,從而找到OOM的原因。可以使用以下方式:
mat:eclipse memory analyzer, 基於eclipse RCP的記憶體分析工具。具體使用參考:http://www.eclipse.org/mat/
jhat:JDK自帶的java heap analyze tool,可以將堆中的物件以html的形式顯示出來,包括物件的數量,大小等等,並支援物件查詢語言OQL,分析相關的應用後,可以通過http://localhost:7000來訪問分析結果。不推薦使用。
OOM常見原因及解決方案
釋放記憶體
在Linux系統下,我們一般不需要去釋放記憶體,因為系統已經將記憶體管理的很好。但是凡事也有例外,有的時候記憶體會被快取佔用掉,導致系統使用SWAP空間影響效能,例如當你在linux下頻繁存取檔案後,實體記憶體會很快被用光,當程式結束後,記憶體不會被正常釋放,而是一直作為caching。此時就需 要執行釋放記憶體(清理快取)的操作了。
釋放記憶體操作:
sync # 強制將記憶體中的快取寫入磁碟
echo 數字 > /proc/sys/vm/drop_caches #數字可以是0-3的整數
數字含義:
- 0:不釋放(系統預設值)
- 1:釋放頁快取
- 2:釋放dentries和inodes
- 3:釋放所有快取