如何使用Hotspot动态附加机制访问JVM内部数据结构?

Nff*_*ff3 0 java debugging jvm jvm-hotspot jvmti

根据OpenJDK 的网站,可以将线程附加到 Hotspot(动态附加 API),该线程可以收集有关它的信息。我在互联网上找不到任何关于如何获取有关 Hotspot 内部数据结构的信息,例如操作数堆栈或字节码解释器的状态(以了解当前正在执行哪个字节码)或检索当前堆栈帧等的材料。

另外,如果动态连接 API 无法实现这一点,那么如何使用可服务性代理来完成此操作?我在互联网上找到的唯一示例是来自 Github 的要点,它展示了如何附加到正在运行的 JVM 并获取某些字段的值。那么如何访问JVM中的上述内部数据结构呢?

apa*_*gin 5

创建您自己的调试工具一文简要介绍了动态连接和可服务性代理。

动态连接允许连接到正在运行的 JVM 并执行预定义命令之一,例如

  • 打印堆栈跟踪
  • 转储堆
  • 查询或设置VM标志
  • 加载代理库
  • ETC。

基本上,标准jstackjmap工具jcmd几乎涵盖了 Dynamic Attach 提供的所有功能。此 API不适用于访问内部 JVM 结构。我怀疑除了加载自定义 JVM TI 库之外,它能否帮助您完成任务。


Serviceability Agent更接近JVM内部结构。事实上,它可以读取 JVM 内存并恢复代码缓存、堆栈帧、TLAB、常量池等结构。

SA javadoc 可在此处获取。JDK 源代码中有一些基于 SA 的工具的示例。

但是SA也不能满足你的要求。

  1. 它是一个只读接口。
  2. 它在流程之外起作用。基于 SA 的工具完全挂起 JVM 进程并使用ptrace读取其内存。
  3. 这是相当慢的。它的主要目的是调试无响应(或死机)的 JVM 进程。

关于操作数栈、字节码指针等,这些概念只存在于解释器中。一旦方法经过 JIT 编译,它就不再具有您所询问的结构。

  • 局部变量和操作数可以分配在 CPU 寄存器中或转换为常量。
  • 机器代码并不总是一对一映射到字节码。
  • 内联方法甚至可能没有自己的堆栈帧,等等。

逐一执行字节码就意味着放弃 JIT 编译。JVM TI SingleStep确实只能在解释器中工作。Java 应用程序在纯解释模式下运行速度可能会慢 10-100 倍。

如果您想保持调试器的性能合理,那么逐个处理每个字节码指令并不是一种选择。如前所述,仪器仪表是正确的方法。请注意,没有必要拦截每个字节码 - 检测基本块就足够了。