天天看點

jhat(Unrecognized magic number: 170..) jproflier(the selected snapshot does not have a valid..)

環境

centos7 + java8 + tomcat8

分析dump檔案

通過配置jvm的:-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/xxx/xxx.dump

或者通過jmap -dump:live,format=b,file=xxx.dump指令生成dump檔案

生成的dump檔案比較大,是以在伺服器上,通過tar -czvf error.dump.gz error.dump壓縮dump檔案,并拉取到本地(通過sz指令将壓縮檔案發送到本地,然後通過winrar解壓)。之後通過jhat或者jprofiler(推薦)分析記憶體鏡像。

dump解析問題

        1. 通過jprofiler分析,将error.dump字尾改為hprof,報錯提示:the selected snapshot does not have a valid format

        2. 通過jhat error.dump,報錯提示:Unrecognized magic number: 1701999215

PS C:\Users\Public\Desktop> jhat.exe C:\Users\zhufeifei\Desktop\error.dump
Reading from C:\Users\zhufeifei\Desktop\error.dump...
java.io.IOException: Unrecognized magic number: 1701999215
        at com.sun.tools.hat.internal.parser.Reader.readFile(Reader.java:94)
        at com.sun.tools.hat.Main.main(Main.java:159)
           

出現上述問題的原因是dump檔案頭資訊不對,正常生成的dump檔案頭以0x4a415641 開頭,是以分析工具在執行解析時會首先解析檔案頭是否為合法檔案,如果不合法則出現上出問題。

處理方式1:

       将壓縮檔案名xxx.dump.gz更改為,xxx.dump.tar.gz,注意:最重要的時字尾tar.gz,決定能否在winrar上正确的進行解壓和解包操作。原因見最後思考總結。

處理方式2:

        檢視dump檔案的16進制形式,來确定頭是否是0x4a415641開始

        檢視方式:(git bash指令行, dump檔案較大,不推薦使用可視化編輯工具)

$ xxd error.dump | head -35
00000000: 6572 726f 722e 646d 7000 0000 0000 0000  error.dmp.......
00000010: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000020: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000030: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000040: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000060: 0000 0000 3030 3030 3630 3000 3030 3030  ....0000600.0000
00000070: 3030 3000 3030 3030 3030 3000 3335 3430  000.0000000.3540
00000080: 3137 3131 3735 3000 3134 3130 3031 3534  1711750.14100154
00000090: 3237 3100 3031 3134 3232 0020 3000 0000  271.011422. 0...
000000a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000100: 0075 7374 6172 2020 0072 6f6f 7400 0000  .ustar  .root...
00000110: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000120: 0000 0000 0000 0000 0072 6f6f 7400 0000  .........root...
00000130: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000140: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000150: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000160: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000170: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000180: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000190: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000200: 4a41 5641 2050 524f 4649 4c45 2031 2e30  JAVA PROFILE 1.0
00000210: 2e32 0000 0000 0800 0001 7aeb 4e36 8801  .2........z.N6..
00000220: 0000 0000 0000 0078 0000 7f41 47f3 1170  .......x...AG..p
           

可以發現此檔案頭并不是0x4a415641, 而真正的檔案頭在00000200處開始,是以隻要将200偏移量之前的資料删除即可,左側的為位址偏移量,16進制表示形式,首先要轉換為10進制。

$ printf %d 0x200
512
           

二進制檔案截取,跳過512個位元組

$ dd if=error.dump of=head.dump bs=1k count=1 skip=512 iflag=skip_bytes
1+0 records in
1+0 records out
1024 bytes (1.0 kB, 1.0 KiB) copied, 0.0011282 s, 908 kB/s
           

檢視 最終生成的檔案頭資訊,可以看到檔案頭為0x4a415641開始

$ xxd error.dmp | head
00000000: 4a41 5641 2050 524f 4649 4c45 2031 2e30  JAVA PROFILE 1.0
00000010: 2e32 0000 0000 0800 0001 7aeb 4e36 8801  .2........z.N6..
00000020: 0000 0000 0000 0078 0000 7f41 47f3 1170  .......x...AG..p
00000030: 284c 6a61 7661 2f6c 616e 672f 5374 7269  (Ljava/lang/Stri
00000040: 6e67 3b4c 6a61 7661 2f75 7469 6c2f 5365  ng;Ljava/util/Se
00000050: 743c 4c6a 6176 612f 6c61 6e67 2f53 7472  t<Ljava/lang/Str
           

此後可以通過jhat或者jprofiler解析記憶體快照了。

思考&總結 

        上述問題隻出現在window機器上解壓後,格式不正确,在linux上通過tar -xvf解壓得到的檔案頭是正常的。

        經過測試,發現出現此問題的原因是在伺服器上通過指令: tar -czvf error.dump.gz error.dump,生成的壓縮檔案為error.dump.gz,到window上通過winrar解壓出的檔案頭部無故多了檔案名和檔案屬性資訊,導緻dump檔案無法解析。

        解決方式:tar -czvf error.dump.tar.gz error.dump, 壓縮檔案名更改為xxx.tar.gz,在window上解壓就沒有問題了。由于tar是壓縮和打包,是以僅命名為gz字尾時,winrar隻進行解壓并不會進行解包操作,導緻附屬在源檔案頭部的打包資訊沒有被正确的讀取移除掉。

參考:stackoverflow