「JVM 故障诊断」HotSpot VM 插件与工具

在 HotSpot 研发过程中,开发团队曾经编写(或收集)过不少 JVM 插件和辅助工具,它们存放在 HotSpot 源码 hotspot/src/share/tools 目录下;
将编译好的插件放到 JDK_HOME/jre/bin/server 目录(JDK 9以下)或 JDK_HOME/lib/amd64/server(JDK 9或以上)即可使用;

  • Ideal Graph Visualizer: 用于可视化展示 C2 即时编译器将字节码转化为理想图,然后转化为机器码的过程;
  • Client Compiler Visualizer: 用于查看 C1 即时编译器生成高级中间表示(HIR),转换成低级中间表示(LIR)和做物理寄存器分配的过程;
  • MakeDeps: 帮助处理 HotSpot 的编译依赖;
  • Project Creator: 帮忙生成 Visual Studio.project
  • LogCompilation: 将 -XX:+LogCompilation 输出的日志整理成易读的格式;
  • HSDIS: 即时编译器的反汇编插件;

文章目录

1. HSDIS

即时编译器的反汇编插件;把即时编译器动态生成的本地代码还原成汇编代码输出,附带大量注释;

代码示例

public class Bar {
    int a = 1;
    static int b = 2;

    public int sum(int c) {
        return a + b + c;
    }

    public static void main(String[] args) {
        new Bar().sum(3);
    }
}

执行编译

# -Xcomp 以编译模式执行代码,直接触发即时编译
# -XX:CompileCommand 让编译器不要内联 sum() 并只编译 sum()
# 输出反编译内容
# Product 版 VM 需要额外开启
java -XX:+PrintAssembly -XX:+UnlockDiagnosticVMOptions -Xcomp -XX:CompileCommand=dontinline,*Bar.sum -XX:CompileCommand=compileonly,*Bar.sum

反汇编结果

[Disassembling for mach='i386']
[Entry Point]
[Constants]
    # {method} 'sum' '(I)I' in 'test/Bar'
    # this: ecx = 'test/Bar'
    # parm0: edx = int
    #       [sp+0x20] (sp of caller)
    ......
    0x01cac407: cmp     0x4(%ecx),%eax
    0x01cac40a: jne     0x01c6b050          ; {runtime_call}
[Verified Entry Point]
    0x01cac410: mov     %eax,-0x8000(%esp)
    0x01cac417: push    %ebp
    0x01cac418: sub     $0x18,%esp          ; *aload_0
                                            ; - test.Bar::sum@0 (line 8)

    ;; block B0 [0, 10]

    0x01cac41b: mov     0x8(%ecx),%eax      ; *getfield a
                                            ; - test.Bar::sum@1 (line 8)
    0x01cac41e: mov     $0x3d2fad8,%esi     ; {oop(a 'java/lang/Class' = 'test/Bar')}
    0x01cac423: mov     0x68(%esi),%esi     ; *getstatic b
                                            ; - test.Bar::sum@4 (line 8)
    0x01cac426: add     %edx,%eax
    0x01cac428: add     %esi,%eax
    0x01cac42a: add     $0x18,%esp
    0x01cac42d: pop     %ebp
    0x01cac42e: test    %eax,0x2b0100       ; {poll_return}
    0x01cac434: ret
  • mov %eax,-0x8000(%esp): 检查栈溢;
  • push %ebp: 保存上一栈帧基址;
  • sub $0x18,%esp: 给新帧分配空间;
  • mov 0x8(%ecx),%eax: 取实例变量 a,这里 0x8(%ecx) 就是 ecx+0x8 的意思,前面代码片段 [Constants] 中提示了 this:ecx='test/Bar',即 ecx 寄存器中放的就是 this 对象的地址;偏移 0x8 是越过 this 对象的对象头,之后就是实例变量 a 的内存位置;这次是访问 Java 堆中的数据;
  • mov $0x3d2fad8,%esi: 取 test.Bar 在方法区的指针;
  • mov 0x68(%esi),%esi: 取类变量 b,这次是访问方法区中的数据;
  • add %esi,%eax、add%edx,%eax: 做 2 次加法,求 a+b+c 的值,前面的代码把 a 放在 eax 中,把 b 放在 esi 中,而 c 在 [Constants] 中提示了,parm0:edx=int,说明 c 在 edx 中;
  • add $0x18,%esp: 撤销栈帧;
  • pop %ebp: 恢复上一栈帧;
  • test %eax,0x2b0100: 轮询方法返回处的 SafePoint;
  • ret: 方法返回;

2. JITWatch

经常与 HSDIS 搭配使用的可视化变异日志分析工具,可以方便的查看相应类和方法的 Java 源码、字节码、即时编译生成的汇编代码等;

VM Arguments 设置

-XX:+UnlockDiagnosticVMOptions
-XX:+TraceClassLoading
-XX:+LogCompilation
-XX:LogFile=/tmp/logfile.log
-XX:+PrintAssembly

上一篇:「JVM 故障诊断」可视化工具
下一篇:「JVM 故障诊断」故障分析与处理案例

PS:感谢每一位志同道合者的阅读,欢迎关注、评论、赞!


参考资料:

  • [1]《深入理解 Java 虚拟机》
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Aurelius-Shu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值