原創(chuàng):扣釘日記(微信公眾號ID:codelogs),歡迎分享,非公眾號轉載保留此聲明。
在之前的OOM問題復盤中,我們添加了jmap腳本來自動dump內存現場,方便排查OOM問題。
但當我反復模擬OOM場景測試時,發(fā)現jmap有時可以dump成功,有時會報錯,如下:
經過網上一頓搜索,發(fā)現兩種原因可能導致這個問題,一是執(zhí)行jmap用戶與jvm進程用戶不一致,二是/tmp/.java_pidXXX
文件被刪除,但經過檢查,這都不是我們jmap失敗的原因。
經過了解,jmap導出內存的原理,大致如下:
- 如果jvm進程id是8255,jmap會先創(chuàng)建一個
/tmp/.java_pid8255
文件,然后發(fā)送SIGQUIT信號給jvm。 - jvm收到信號后啟動AttachListener線程,以UNIX domain socket的形式監(jiān)聽
/tmp/.java_pid8255
文件,以接收命令。 - jmap也以UNIX domain socket的形式連接上
/tmp/.java_pid8255
文件,并發(fā)送dumpheap命令給jvm,這個過程中jvm會檢查命令發(fā)送方用戶的euid/egid是否與自己一致。 - AttachListener線程收到dumpheap命令后,等到JVM進入Safepoint后,執(zhí)行HeapDumper操作以導出heap.hprof文件。
可以看出,當jvm已經卡死,或有長時間的GC正在Safepoint中執(zhí)行,都會導致jmap長時間讀不到命令的響應而超時失??!
使用jmap -F
當給jmap添加-F
參數時,jmap會使用Linux的ptrace
機制來導出堆內存,ptrace
是Linux平臺的一種調試機制,像strace、gdb都是基于它開發(fā)的,它使得調試進程(jmap)可以直接讀取被調試進程(jvm)的原生內存,然后jmap再根據jvm的內存布局規(guī)范,將原生內存轉換為hprof格式。
但在實際執(zhí)行時,會發(fā)現jmap -F
執(zhí)行得非常慢,可能要幾個小時,這是因為ptrace
每次只能讀一個字的內存,而我們的堆有10G,因此jmap -F
對于我們幾乎無法使用。
注:這里說的原生程序,指的是類似于C/C++這種直接編譯出來、不需要依賴語言虛擬機的程序,而原生內存,指的是通過malloc或mmap等直接申請出來的內存。
使用gcore
有過Linux下原生程序調試經驗的,應該會知道gcore這個實用工具,它可用來生成程序原生內存的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文件轉換為hprof文件,很慢,建議摘流量后執(zhí)行
$ jmap -dump:format=b,file=heap.hprof `which java` core.8787
但是當我使用jmap轉換core文件時,我發(fā)現我本機測試時可以成功,但在測試服務器上卻一直報錯,如下:
我網上找了好久,都沒找到報此錯誤的原因...
但我發(fā)現gcore執(zhí)行時,是有一些警告信息的,如下:
看起來可能是gcore導出的core文件不全,聯(lián)想到jvm部署在容器中,懷疑是有某些權限限制,導致部分程序內存導出失敗了。
使用Linux內核的coredump機制
除了gcore可以導原生內存,其實Linux內核也有自動的coredump機制,即進程在收到某些信號后,會自動觸發(fā)內核的coredump機制,內核會負責將進程的原生內存保存為core文件,而內核一般是最高權限運行的,所以它生成的core文件應該是完整的。
先開啟coredump機制,如下:
# 檢查是否開啟,輸出unlimited表示core文件不受限制,即完全開啟
$ ulimit -c
# 臨時開啟coredump
$ ulimit -c unlimited
# 永久開啟
$ echo "ulimit -c unlimited" >> /etc/profile
然后,配置一下coredump文件保存位置,如下:
# 查看當前配置
$ 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 |
注:如果沒有權限修改core_pattern路徑,可考慮使用軟鏈接
ln -s
做路徑跳轉,當然,還需要保證coredump路徑有寫入權限。
配置ok后,可通過kill發(fā)送信號來觸發(fā)內核coredump,可觸發(fā)coredump的常見信號如下:
- SIGQUIT 數值2 從鍵盤輸入
Ctrl+'\'
可以產生此信號 - SIGILL 數值4 非法指令
- SIGABRT 數值6 abort調用
- SIGSEGV 數值11 非法內存訪問
- SIGTRAP 數值5 調試程序時使用的斷點
我選擇了SIGABRT信號,即kill -6
,經過驗證,可生成core文件,而且core文件也能被jmap轉換為hprof文件。文章來源:http://www.zghlxwxcb.cn/news/detail-415334.html
有了hprof文件,就可以愉快地使用MAT、JVisualVM、JMC等工具進行內存分析啦??文章來源地址http://www.zghlxwxcb.cn/news/detail-415334.html
到了這里,關于jmap執(zhí)行失敗了,怎么獲取heapdump?的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!