centos7 + java8 + tomcat8
通过配置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(推荐)分析内存镜像。
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 开头,所以分析工具在执行解析时会首先解析文件头是否为合法文件,如果不合法则出现上出问题。
将压缩文件名xxx.dump.gz更改为,xxx.dump.tar.gz,注意:最重要的时后缀tar.gz,决定能否在winrar上正确的进行解压和解包操作。原因见最后思考总结。
查看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只进行解压并不会进行解包操作,导致附属在源文件头部的打包信息没有被正确的读取移除掉。