jmap執(zhí)行失敗如何獲取heapdump詳解
正文
在之前的OOM問題復(fù)盤中,我們添加了jmap腳本來自動(dòng)dump內(nèi)存現(xiàn)場,方便排查OOM問題。
但當(dāng)我反復(fù)模擬OOM場景測試時(shí),發(fā)現(xiàn)jmap有時(shí)可以dump成功,有時(shí)會(huì)報(bào)錯(cuò),如下:
經(jīng)過網(wǎng)上一頓搜索,發(fā)現(xiàn)兩種原因可能導(dǎo)致這個(gè)問題,一是執(zhí)行jmap用戶與jvm進(jìn)程用戶不一致,二是/tmp/.java_pidXXX
文件被刪除,但經(jīng)過檢查,這都不是我們jmap失敗的原因。
經(jīng)過了解,jmap導(dǎo)出內(nèi)存的原理,大致如下:
- 如果jvm進(jìn)程id是8255,jmap會(huì)先創(chuàng)建一個(gè)
/tmp/.java_pid8255
文件,然后發(fā)送SIGQUIT信號(hào)給jvm。 - jvm收到信號(hào)后啟動(dòng)AttachListener線程,以UNIX domain socket的形式監(jiān)聽
/tmp/.java_pid8255
文件,以接收命令。 - jmap也以UNIX domain socket的形式連接上
/tmp/.java_pid8255
文件,并發(fā)送dumpheap命令給jvm,這個(gè)過程中jvm會(huì)檢查命令發(fā)送方用戶的euid/egid是否與自己一致。 - AttachListener線程收到dumpheap命令后,等到JVM進(jìn)入Safepoint后,執(zhí)行HeapDumper操作以導(dǎo)出heap.hprof文件。
可以看出,當(dāng)jvm已經(jīng)卡死,或有長時(shí)間的GC正在Safepoint中執(zhí)行,都會(huì)導(dǎo)致jmap長時(shí)間讀不到命令的響應(yīng)而超時(shí)失??!
使用jmap -F
當(dāng)給jmap添加-F
參數(shù)時(shí),jmap會(huì)使用Linux的ptrace
機(jī)制來導(dǎo)出堆內(nèi)存,ptrace
是Linux平臺(tái)的一種調(diào)試機(jī)制,像strace、gdb都是基于它開發(fā)的,它使得調(diào)試進(jìn)程(jmap)可以直接讀取被調(diào)試進(jìn)程(jvm)的原生內(nèi)存,然后jmap再根據(jù)jvm的內(nèi)存布局規(guī)范,將原生內(nèi)存轉(zhuǎn)換為hprof格式。
但在實(shí)際執(zhí)行時(shí),會(huì)發(fā)現(xiàn)jmap -F
執(zhí)行得非常慢,可能要幾個(gè)小時(shí),這是因?yàn)?code>ptrace每次只能讀一個(gè)字的內(nèi)存,而我們的堆有10G,因此jmap -F
對于我們幾乎無法使用。
注:這里說的原生程序,指的是類似于C/C++這種直接編譯出來、不需要依賴語言虛擬機(jī)的程序,而原生內(nèi)存,指的是通過malloc或mmap等直接申請出來的內(nèi)存。
使用gcore
有過Linux下原生程序調(diào)試經(jīng)驗(yàn)的,應(yīng)該會(huì)知道gcore這個(gè)實(shí)用工具,它可用來生成程序原生內(nèi)存的core文件,然后jstack、jmap等都可以讀取此類文件,如下:
# 生成core文件,8787是進(jìn)程號(hào) $ gcore -o core 8787 Saved corefile core.8787 [Inferior 1 (process 8787) detached] $ ll -lh core.8787 -rw-r--r-- 1 work work 5.8G 2023-04-16 11:40:00 core.8787 # 從core文件中讀取線程棧 $ jstack `which java` core.8787 # 將core文件轉(zhuǎn)換為hprof文件,很慢,建議摘流量后執(zhí)行 $ jmap -dump:format=b,file=heap.hprof `which java` core.8787
但是當(dāng)我使用jmap轉(zhuǎn)換core文件時(shí),我發(fā)現(xiàn)我本機(jī)測試時(shí)可以成功,但在測試服務(wù)器上卻一直報(bào)錯(cuò),如下:
我網(wǎng)上找了好久,都沒找到報(bào)此錯(cuò)誤的原因...
但我發(fā)現(xiàn)gcore執(zhí)行時(shí),是有一些警告信息的,如下:
看起來可能是gcore導(dǎo)出的core文件不全,聯(lián)想到j(luò)vm部署在容器中,懷疑是有某些權(quán)限限制,導(dǎo)致部分程序內(nèi)存導(dǎo)出失敗了。
使用Linux內(nèi)核的coredump機(jī)制
除了gcore可以導(dǎo)原生內(nèi)存,其實(shí)Linux內(nèi)核也有自動(dòng)的coredump機(jī)制,即進(jìn)程在收到某些信號(hào)后,會(huì)自動(dòng)觸發(fā)內(nèi)核的coredump機(jī)制,內(nèi)核會(huì)負(fù)責(zé)將進(jìn)程的原生內(nèi)存保存為core文件,而內(nèi)核一般是最高權(quán)限運(yùn)行的,所以它生成的core文件應(yīng)該是完整的。
先開啟coredump機(jī)制,如下:
# 檢查是否開啟,輸出unlimited表示core文件不受限制,即完全開啟 $ ulimit -c # 臨時(shí)開啟coredump $ ulimit -c unlimited # 永久開啟 $ echo "ulimit -c unlimited" >> /etc/profile
然后,配置一下coredump文件保存位置,如下:
# 查看當(dāng)前配置 $ cat /proc/sys/kernel/core_pattern /home/core/core.%e.%p.%t # 配置coredump文件保存位置,并使其生效 $ vi /etc/sysctl.conf kernel.core_pattern=/home/core/core.%e.%p.%t $ sysctl –p /etc/sysctl.conf
core_pattern占位符解釋
占位符 | 解釋 |
---|---|
%p | pid |
%u | uid |
%g | gid |
%s | signal number |
%t | UNIX time of dump |
%h | hostname |
%e | executable filename |
注:如果沒有權(quán)限修改core_pattern路徑,可考慮使用軟鏈接ln -s
做路徑跳轉(zhuǎn),當(dāng)然,還需要保證coredump路徑有寫入權(quán)限。
配置ok后,可通過kill發(fā)送信號(hào)來觸發(fā)內(nèi)核coredump,可觸發(fā)coredump的常見信號(hào)如下:
- SIGQUIT 數(shù)值2 從鍵盤輸入
Ctrl+'\'
可以產(chǎn)生此信號(hào) - SIGILL 數(shù)值4 非法指令
- SIGABRT 數(shù)值6 abort調(diào)用
- SIGSEGV 數(shù)值11 非法內(nèi)存訪問
- SIGTRAP 數(shù)值5 調(diào)試程序時(shí)使用的斷點(diǎn)
我選擇了SIGABRT信號(hào),即kill -6
,經(jīng)過驗(yàn)證,可生成core文件,而且core文件也能被jmap轉(zhuǎn)換為hprof文件。
有了hprof文件,就可以愉快地使用MAT、JVisualVM、JMC等工具進(jìn)行內(nèi)存分析啦??
以上就是jmap執(zhí)行失敗如何獲取heapdump詳解的詳細(xì)內(nèi)容,更多關(guān)于jmap獲取heapdump的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
org.slf4j.Logger中info()方法的使用詳解
這篇文章主要介紹了org.slf4j.Logger中info()方法的使用詳解,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12SpringBoot通過自定義注解實(shí)現(xiàn)參數(shù)校驗(yàn)
實(shí)現(xiàn)參數(shù)校驗(yàn)說實(shí)話方式還挺多,個(gè)人使用過直接在Controller代碼里面寫、AOP+自定義注解、ConstraintValidator。本文主要和大家講的是ConstraintValidator實(shí)現(xiàn),感興趣的可以了解一下2022-12-12實(shí)例詳解Java實(shí)現(xiàn)圖片與base64字符串之間的轉(zhuǎn)換
這篇文章主要介紹了Java實(shí)現(xiàn)圖片與base64字符串之間的轉(zhuǎn)換實(shí)例代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下2016-12-12spring集成mybatis實(shí)現(xiàn)mysql數(shù)據(jù)庫讀寫分離
本文通過實(shí)例代碼給大家介紹了spring集成mybatis實(shí)現(xiàn)mysql數(shù)據(jù)庫讀寫分離,需要的朋友可以參考下2017-08-08