jmap執(zhí)行失敗了,怎么獲取heapdump?

在之前的OOM問題復(fù)盤中,我們添加了jmap腳本來自動dump內(nèi)存現(xiàn)場,方便排查OOM問題。

但當(dāng)我反復(fù)模擬OOM場景測試時,發(fā)現(xiàn)jmap有時可以dump成功,有時會報錯,如下:

經(jīng)過網(wǎng)上一頓搜索,發(fā)現(xiàn)兩種原因可能導(dǎo)致這個問題,一是執(zhí)行jmap用戶與jvm進程用戶不一致,二是/tmp/.java_pidXXX文件被刪除,但經(jīng)過檢查,這都不是我們jmap失敗的原因。

經(jīng)過了解,jmap導(dǎo)出內(nèi)存的原理,大致如下:

  1. 如果jvm進程id是8255,jmap會先創(chuàng)建一個/tmp/.java_pid8255文件,然后發(fā)送SIGQUIT信號給jvm。
  2. jvm收到信號后啟動AttachListener線程,以UNIX domain socket的形式監(jiān)聽/tmp/.java_pid8255文件,以接收命令。
  3. jmap也以UNIX domain socket的形式連接上/tmp/.java_pid8255文件,并發(fā)送dumpheap命令給jvm,這個過程中jvm會檢查命令發(fā)送方用戶的euid/egid是否與自己一致。
  4. AttachListener線程收到dumpheap命令后,等到JVM進入Safepoint后,執(zhí)行HeapDumper操作以導(dǎo)出heap.hprof文件。

可以看出,當(dāng)jvm已經(jīng)卡死,或有長時間的GC正在Safepoint中執(zhí)行,都會導(dǎo)致jmap長時間讀不到命令的響應(yīng)而超時失?。?/p>

使用jmap -F

當(dāng)給jmap添加-F參數(shù)時,jmap會使用Linux的ptrace機制來導(dǎo)出堆內(nèi)存,ptrace是Linux平臺的一種調(diào)試機制,像strace、gdb都是基于它開發(fā)的,它使得調(diào)試進程(jmap)可以直接讀取被調(diào)試進程(jvm)的原生內(nèi)存,然后jmap再根據(jù)jvm的內(nèi)存布局規(guī)范,將原生內(nèi)存轉(zhuǎn)換為hprof格式。

但在實際執(zhí)行時,會發(fā)現(xiàn)jmap -F執(zhí)行得非常慢,可能要幾個小時,這是因為ptrace每次只能讀一個字的內(nèi)存,而我們的堆有10G,因此jmap -F對于我們幾乎無法使用。

注:這里說的原生程序,指的是類似于C/C++這種直接編譯出來、不需要依賴語言虛擬機的程序,而原生內(nèi)存,指的是通過malloc或mmap等直接申請出來的內(nèi)存。

使用gcore

有過Linux下原生程序調(diào)試經(jīng)驗的,應(yīng)該會知道gcore這個實用工具,它可用來生成程序原生內(nèi)存的core文件,然后jstack、jmap等都可以讀取此類文件,如下:

# 生成core文件,8787是進程號
$ 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文件時,我發(fā)現(xiàn)我本機測試時可以成功,但在測試服務(wù)器上卻一直報錯,如下:

我網(wǎng)上找了好久,都沒找到報此錯誤的原因...

但我發(fā)現(xiàn)gcore執(zhí)行時,是有一些警告信息的,如下:


看起來可能是gcore導(dǎo)出的core文件不全,聯(lián)想到j(luò)vm部署在容器中,懷疑是有某些權(quán)限限制,導(dǎo)致部分程序內(nèi)存導(dǎo)出失敗了。

使用Linux內(nèi)核的coredump機制

除了gcore可以導(dǎo)原生內(nèi)存,其實Linux內(nèi)核也有自動的coredump機制,即進程在收到某些信號后,會自動觸發(fā)內(nèi)核的coredump機制,內(nèi)核會負責(zé)將進程的原生內(nèi)存保存為core文件,而內(nèi)核一般是最高權(quán)限運行的,所以它生成的core文件應(yīng)該是完整的。

先開啟coredump機制,如下:

# 檢查是否開啟,輸出unlimited表示core文件不受限制,即完全開啟
$ ulimit -c

# 臨時開啟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ā)送信號來觸發(fā)內(nèi)核coredump,可觸發(fā)coredump的常見信號如下:

  • SIGQUIT 數(shù)值2 從鍵盤輸入Ctrl+'\'可以產(chǎn)生此信號
  • SIGILL 數(shù)值4 非法指令
  • SIGABRT 數(shù)值6 abort調(diào)用
  • SIGSEGV 數(shù)值11 非法內(nèi)存訪問
  • SIGTRAP 數(shù)值5 調(diào)試程序時使用的斷點

我選擇了SIGABRT信號,即kill -6,經(jīng)過驗證,可生成core文件,而且core文件也能被jmap轉(zhuǎn)換為hprof文件。

有了hprof文件,就可以愉快地使用MAT、JVisualVM、JMC等工具進行內(nèi)存分析啦??

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容