Java 技巧合集
获取一直FullGC下的java进程HeapDump的小技巧
就是小技巧,操作步骤需要查询,随手记录
- 找到java进程,gdb attach上去, 例如
gdb -p 12345
- 找到这个
HeapDumpBeforeFullGC
的地址(这个flag如果为true,会在FullGC之前做HeapDump,默认是false)
1 | (gdb) p &HeapDumpBeforeFullGC |
- Copy 地址:0x7f7d50fc660f
- 然后把他设置为true,这样下次FGC之前就会生成一份dump文件
1 | (gdb) set *0x7f7d50fc660f = 1 |
- 最后,等一会,等下次FullGC触发,你就有HeapDump了!
(如果没有指定heapdump的名字,默认是 java_pidxxx.hprof)
(PS. jstat -gcutil pid
可以查看gc的概况)
(操作完成后记得gdb上去再设置回去,不然可能一直fullgc,导致把磁盘打满).
其它
在jvm还有响应的时候可以: jinfo -flag +HeapDumpBeforeFullGC pid 设置HeapDumpBeforeFullGC 为true(- 为false,+-都不要为只打印值)
kill -3 产生coredump 存放在 kernel.core_pattern=/root/core (/etc/sysctl.conf , 先 ulimit -c unlimited;或者 gcore id 获取coredump)
得到core文件后,采用 gdb -c 执行文件 core文件 进入调试模式,对于java,有以下2个技巧:
进入gdb调试模式后,输入如下命令: info threads,观察异常的线程,定位到异常的线程后,则可以输入如下命令:thread 线程编号,则会打印出当前java代码的工作流程。
而对于这个core,亦可以用jstack jmap打印出堆信息,线程信息,具体命令:
jmap -heap 执行文件 core文件 jstack -F -l 执行文件 core文件
容器中的进程的话需要到宿主机操作,并且将容器中的 jdk文件夹复制到宿主机对应的位置。
ps auxff |grep 容器id -A10 找到JVM在宿主机上的进程id
coredump
Coredump叫做核心转储,它是进程运行时在突然崩溃的那一刻的一个内存快照。操作系统在程序发生异常而异常在进程内部又没有被捕获的情况下,会把进程此刻内存、寄存器状态、运行堆栈等信息转储保存在一个文件里。
kill -3 产生coredump 存放在 kernel.core_pattern=/root/core (/etc/sysctl.conf , 先 ulimit -c unlimited;)
或者 gcore id 获取coredump
1 | $cat /proc/sys/kernel/core_pattern |
coredump 分析
1 | //打开 coredump |
以上堆栈涉及到Java代码部分都是看不到函数,需要进一步把Java 符号替换进去
coredump 转 jmap hprof
1 | jmap -dump:format=b,file=24086.hprof /opt/taobao/java/bin/java core.24086 |
以上命令输入是 core.24086 这个 coredump,输出是一个 jmap 的dump 24086.hprof
1 | $jmap -J-d64 /opt/taobao/java/bin/java core.24086 |
coredump 生成 java stack
1 | jstack -J-d64 /opt/taobao/java/bin/java core.24086 |
gdb coredump with java symbol
需要安装JVM debug info包,同时要求gdb版本在7.10以上
设置:
home 目录下创建 .gdbinit 然后放入如下内容,libjvm.so-gdb.py 就是 dbg.py 脚本,gdb启动的时候会自动加载这个脚本
1 | $cat ~/.gdbinit |
使用:
1 | gdb -iex "set auto-load safe-path /" /opt/install/java/bin/java ./core.24086 |
G1 GC为什么快
https://ata.alibaba-inc.com/articles/199497
G1比CMS GC 暂停短、更稳定,但是最终吞吐大概率是CMS要好,这是因为G1编译后代码更大
-XX:InlineSmallCode=3000
告诉编译器, 汇编3000字节以内的函数需要被inline, 这个值默认是2000
另外CMS用的是Dirty Card,而G1 为了降低GC时间在Remeber Set(类似Dirty Card)的维护上花了更多的代价
Dirty Card维护代价:
- 会影响code size
Code size影响了inline机会
Code size增大则instruction cache miss几率变大 (几十倍的执行时间差距) - 本身执行mark dirty动作耗时, 这是一个写内存+GC/mutator线程同步的操作, 可以很复杂, 也可以很简单
比如G1为了降低暂停时间,就要尽量控制Remeber Set的更新,所以还需要判断write动作是否真的有必要更新Remeber Set(类似old.ref = null
这种写操作是不需要更新Remeber Set的)
简单说CMS的每次 Dirty Card维护只需要3条汇编,而G1的Remember Set维护需要十多条、几十条汇编