記憶體洩漏排查之:Show me your Memory
java 語言有個神奇的地方,那就是你時不時會去關注下記憶體。(當然了,任何牛逼的同學都應該關注記憶體)
今天我們就來這麼場景吧:某應用運行了一段時間後,ecs監控報警了,記憶體比較高了,怎麼辦?隨著時間的推移,發現記憶體越來越高(但是又不會打到100%),怎麼辦?
凡事講究證據,報警說記憶體緊張就緊張嗎,還得自己去驗一下。
如何確認記憶體問題?這太重要了! 以下是幾種檢視記憶體問題的方法:(愛信不信啊)
1. top 等檢視系統記憶體概況
top:記憶體去,按M按照記憶體大小排序,立馬看到罪魁禍首。具體命令請參考網上資料。
top簡要使用方法如下:
使用格式: top [-] [d] [p] [q] [c] [C] [S] [s] [n] 引數說明: d:指定每兩次螢幕資訊重新整理之間的時間間隔。當然使用者可以使用s互動命令來改變之。 p:通過指定監控程序ID來僅僅監控某個程序的狀態。 q:該選項將使top沒有任何延遲的進行重新整理。如果呼叫程式有超級使用者許可權,那麼top將以儘可能高的優先順序執行。 S:指定累計模式。 s:使top命令在安全模式中執行。這將去除互動命令所帶來的潛在危險。 i:使top不顯示任何閒置或者僵死程序。 c:顯示整個命令列而不只是顯示命令名。 常用命令說明: Ctrl+L:擦除並且重寫螢幕 K:終止一個程序。系統將提示使用者輸入需要終止的程序PID,以及需要傳送給該程序什麼樣的訊號。一般的終止程序可以使用15訊號;如果不能正常結束那就使用訊號9強制結束該程序。預設值是訊號15。在安全模式中此命令被遮蔽。 i:忽略閒置和僵死程序。這是一個開關式命令。 q:退出程式 r:重新安排一個程序的優先級別。系統提示使用者輸入需要改變的程序PID以及需要設定的程序優先順序值。輸入一個正值將使優先順序降低,反之則可以使該程序擁有更高的優先權。預設值是10。 S:切換到累計模式。 s:改變兩次重新整理之間的延遲時間。系統將提示使用者輸入新的時間,單位為s。如果有小數,就換算成m s。輸入0值則系統將不斷重新整理,預設值是5 s。需要注意的是如果設定太小的時間,很可能會引起不斷重新整理,從而根本來不及看清顯示的情況,而且系統負載也會大大增加。 f或者F:從當前顯示中新增或者刪除專案。 o或者O:改變顯示專案的順序 l:切換顯示平均負載和啟動時間資訊。 m:切換顯示記憶體資訊。 t:切換顯示程序和CPU狀態資訊。 c:切換顯示命令名稱和完整命令列。 M:根據駐留記憶體大小進行排序。 P:根據CPU使用百分比大小進行排序。 T:根據時間/累計時間進行排序。 W:將當前設定寫入~/.toprc檔案中。
另外在記憶體檢視方面,還可以使用 free用於快速直接檢視記憶體,還可以看到有多少是系統快取;(系統快取一般不被計入真正已使用記憶體中)
2. jmx 快速發現jvm中的記憶體異常項
jmx,如果開啟了jmx,則我們可以直接通過jvisualvm檢視記憶體,執行緒監控情況,還可以檢視其他jmx指標;
從這裡你可以,看到記憶體的變化趨勢,垃圾回收,cpu變化趨勢等等,很多直觀的問題完全可以在這一環節發現。
另外,你可以通過採集cpu和採集記憶體的方式,發現程式碼中的瓶頸點。
可以說,jmx是我們進行程式碼優化或者引數高估的絕對王者工具。
效能問題,可以適當進行CPU/記憶體取樣,以快速發現瓶頸點!
建議新增外掛:
Btrace Workbench 用於遠端除錯,也許有用;
BufferMonitor 用於檢視堆外記憶體情況,其實可能不準;
Threads Inspector 用於快速檢視各執行緒情況;
VisualJVM-MBeans 用於檢視 jmx 暴露出來的 指標資訊,可作為業務監控使用;
3. jmap dump 詳細分析jvm的記憶體使用情況
jmap dump,發現記憶體過,其他方面沒啥思路,那就jvm記憶體dump下來,慢慢分析。
dump整個記憶體下來,全量分析,jmap -dump:format=b,file=/tmp/a.dump . 然後就可以使用jvm記憶體分析工具進行分析了,如 mat 。分析工具的技巧可能還是需要去掌握下的,不過我這裡簡單提兩個點,一個就是看得到的堆記憶體,一個不可達的堆記憶體,分析時就注意這兩點。一般可達堆記憶體是很好分析的,不可達堆記憶體則要憑藉一定的經驗才能發現問題了。
對於快速查詢,則直接在伺服器上使用 jmap -heap 就可以查看了。
jmap -dump:format=b,file=/tmp/a.dump <pid> # dump倒是堆記憶體 jmap -heap <pid> # 直接在伺服器上檢視堆的使用情況
4. lsof 列舉出正在使用的檔案,看看是否能發現一些端倪
lsof,這個工具用於排查是否存在很在很多超出預料的檔案的情況,比如開啟某檔案未關閉,建立很多的socket連線等等。當然,發現問題只能靠眼力勁了。
lsof -p <pid> #檢視程序開啟的檔案情況。
lsof 使用詳細介紹參考網上資料: https://www.cnblogs.com/sparkbj/p/7161669.html
5. pmap 檢視程序記憶體概要
pmap,用於檢視程序的記憶體映像資訊, 發現記憶體中大塊的佔用所在,以及分析記憶體可能存在的異常。
從中,你可以看到哪些記憶體上面佔用了多少記憶體,正常的記憶體如 JVM 所在記憶體段,應該是和你的堆記憶體一致的,而其他記憶體段,則是你看不到的地方,這些地方將是你排查記憶體洩漏的方向。
簡要命令下: pmap [ -x | -d ] [ -q ] pids... 結果樣例如下: [root@abtest ~]# pmap -x 27466 27466: /usr/local/jdk1.8.0_211/bin/java -Dzookeeper.log.dir=. -Dzookeeper.root.logger=INFO,CONSOLE -cp /opt/zookeeper/zookeeper-3.4.14/bin/../zookeeper-server/target/classes:/opt/zookeeper/zookeeper-3.4.14/bin/../build/classes:/opt/zookeeper/zookeeper-3.4.14/bin/../zookeeper-server/target/lib/*.jar:/opt/zookeeper/zookeeper-3.4.14/bin/../build/lib/*.jar:/opt/zookeeper/zookeeper-3.4.14/bin/../lib/slf4j-log4j12-1.7.25.jar:/opt/zookeeper/zookeeper-3.4.14/bin/../lib/slf4j-api-1.7.25.jar:/opt/zookeeper/zookeeper-3.4.1 Address Kbytes RSS Dirty Mode Mapping 0000000000400000 4 4 0 r-x-- java 0000000000600000 4 4 4 r---- java 0000000000601000 4 4 4 rw--- java ... 00007fff10253000 136 36 36 rw--- [ stack ] 00007fff102ce000 8 4 0 r-x-- [ anon ] ffffffffff600000 4 0 0 r-x-- [ anon ] ---------------- ------- ------- ------- total kB 5421732 100608 87672
命令操作詳情請參考網上資料:: https://www.cnblogs.com/txw1958/archive/2012/07/26/linux-pmap.html
6. NMT, nativeMemoryTracking jvm 的記憶體追蹤工具
nmt,這個jdk8以後,jvm提供的記憶體跟蹤工具,nativeMemoryTracking, 可以用於排查記憶體方案的問題。
-XX:NativeMemoryTracking=summary # 開啟NMT追蹤 jcmd 1 VM.native_memory summary # 檢視當前的記憶體概況 jcmd 1 VM.native_memory baseline # 建立基準 baseline jcmd 1 VM.native_memory summary.diff # 一段時間後,比對記憶體差異,可以用於發現記憶體的走向問題,如下 [root@abtest ~]# jcmd 5545 VM.native_memory summary.diff 5545: Native Memory Tracking: Total: reserved=5942859KB +2339KB, committed=4104347KB +1519KB - Java Heap (reserved=4194304KB, committed=3645440KB) (mmap: reserved=4194304KB, committed=3645440KB) - Class (reserved=1109328KB +2056KB, committed=66640KB +264KB) (classes #11172 +2) (malloc=1360KB +8KB #15153 +331) (mmap: reserved=1107968KB +2048KB, committed=65280KB +256KB) - Thread (reserved=133119KB, committed=133119KB) (thread #130) (stack: reserved=132544KB, committed=132544KB) (malloc=422KB #652) (arena=152KB #256) - Code (reserved=255919KB +247KB, committed=37519KB +1219KB) (malloc=6319KB +247KB #8248 +270) (mmap: reserved=249600KB, committed=31200KB +972KB) - GC (reserved=209075KB +8KB, committed=188707KB +8KB) (malloc=20659KB +8KB #28406 +320) (mmap: reserved=188416KB, committed=168048KB) - Compiler (reserved=277KB +5KB, committed=277KB +5KB) (malloc=146KB +5KB #592 +9) (arena=131KB #6) - Internal (reserved=12648KB, committed=12648KB) (malloc=12616KB #39090 +5) (mmap: reserved=32KB, committed=32KB) - Symbol (reserved=16444KB +8KB, committed=16444KB +8KB) (malloc=12569KB +8KB #121265 +2) (arena=3876KB #1) - Native Memory Tracking (reserved=3362KB +15KB, committed=3362KB +15KB) (malloc=18KB #204 +2) (tracking overhead=3344KB +15KB) - Arena Chunk (reserved=192KB, committed=192KB) (malloc=192KB) - Unknown (reserved=8192KB, committed=0KB) (mmap: reserved=8192KB, committed=0KB)
如上結果,我們可以得得出些結論,隨著時間的推移, code 部分的佔用空間增加了最多(JIT), Compiler 也增加一些,而堆記憶體則一直保持不變!
7. perf,這是一個性能監控調優工具,但是我們也可能從中發現記憶體問題點
可以先捕獲資料,然後進行效能分析,然後得到可疑的點。
幫助資訊如下:
usage: perf [--version] [--help] [OPTIONS] COMMAND [ARGS] The most commonly used perf commands are: annotate Read perf.data (created by perf record) and display annotated code archive Create archive with object files with build-ids found in perf.data file bench General framework for benchmark suites buildid-cache Manage build-id cache. buildid-list List the buildids in a perf.data file c2c Shared Data C2C/HITM Analyzer. config Get and set variables in a configuration file. data Data file related processing diff Read perf.data files and display the differential profile evlist List the event names in a perf.data file ftrace simple wrapper for kernel's ftrace functionality inject Filter to augment the events stream with additional information kallsyms Searches running kernel for symbols kmem Tool to trace/measure kernel memory properties kvm Tool to trace/measure kvm guest os list List all symbolic event types lock Analyze lock events mem Profile memory accesses record Run a command and record its profile into perf.data report Read perf.data (created by perf record) and display the profile sched Tool to trace/measure scheduler properties (latencies) script Read perf.data (created by perf record) and display trace output stat Run a command and gather performance counter statistics test Runs sanity tests. timechart Tool to visualize total system behavior during a workload top System profiling tool. probe Define new dynamic tracepoints trace strace inspired tool See 'perf help COMMAND' for more information on a specific command.
簡單示例:
perf record -g -e cpu-clock -p 5545 # 記錄程序 5545 的相關效能資訊 perf report -i perf.data # 讀取剛剛記錄的資料,可以顯示出種操作的佔用情況,如下 Samples: 908 of event 'cpu-clock', Event count (approx.): 227000000 Children Self Command Shared Object Symbol + 32.27% 0.00% java libpthread-2.17.so [.] start_thread + 32.27% 0.00% java libjvm.so [.] java_start + 26.54% 0.00% java libjvm.so [.] ConcurrentG1RefineThread::run + 26.54% 0.11% java libjvm.so [.] ConcurrentG1RefineThread::run_young_rs_sampling + 25.77% 5.62% java libjvm.so [.] YoungList::rs_length_sampling_next + 22.58% 0.55% java [kernel.kallsyms] [k] tracesys + 11.01% 0.00% java perf-5545.map [.] 0x00007f553ec1e981 + 10.79% 0.44% java libjvm.so [.] JVM_Sleep + 9.36% 0.55% java libjvm.so [.] G1CollectorPolicy::update_incremental_cset_info + 8.70% 0.55% java libjvm.so [.] os::sleep + 8.26% 0.00% java [unknown] [k] 0xee83b0ac00709650 + 8.15% 0.99% java libpthread-2.17.so [.] pthread_cond_timedwait@@GLIBC_2.3.2 + 7.93% 0.00% java perf-5545.map [.] 0x00007f553f7f7d30
如果運氣碰巧的話,你有可能能查到某些異常的操作,從而推斷出問題所在。
8. gdb 除錯工具dump出可疑記憶體
gdb, linux下強大的除錯工具,但是我們不用它來除錯,我們只用來輸出記憶體的內容。即dump記憶體,前面用到的jmap dump只能看到jvm的記憶體資訊,而gdb則可以看所有的,當然我們會用來看其他部分的記憶體。
gdb attach <pid> # 先連線到程序中 gdb dump memory /path/dump.bin 0x0011 0x0021 # dump 出記憶體段的資訊,具體要 dump 的記憶體段地址,可以藉助之前pmap 排查的結果,以及 cat /proc/<pid>/maps 中指示的地址段得出 strings /path/dump.bin | less # 檢視記憶體內容, 相信你能從中發現一些不一樣的東西
以上,足夠你排查出你懷疑的記憶體洩露問題了。如果不能,那就算了!
嘮叨: 注重時效性!
&n