JDK11+环境下Java Hotspot SA的SystemDictionary.ClassVisitor不可用求解
修复方案
问题根因
JDK9模块化重构后,原SystemDictionary内部定义的ClassVisitor接口被迁移至sun.jvm.hotspot.utilities.ClassVisitor独立接口,allClassesDo方法入参同步修改为新接口类型,导致适配JDK8的代码在JDK11+环境编译时找不到内部类符号。
代码修改
导入新的ClassVisitor接口,调整实例化逻辑,修改后完整代码如下:
package sun.jvm.hotspot.jdi; import sun.jvm.hotspot.oops.InstanceKlass; import sun.jvm.hotspot.oops.Method; import sun.jvm.hotspot.oops.Klass; import sun.jvm.hotspot.memory.SystemDictionary; import sun.jvm.hotspot.runtime.VM; import sun.jvm.hotspot.tools.Tool; import sun.jvm.hotspot.utilities.ClassVisitor; public class GetBytecode extends Tool { @Override public void run() { VM.getVM().getSystemDictionary().allClassesDo(new ClassVisitor() { public void visit(Klass klass) { if (klass.getName().asString().equals("MyDebugger")) { Method method = ((InstanceKlass) klass).findMethod("sendMessage", "()V"); for (byte bc : method.getByteCode()) { System.out.printf("%02x ", bc); } } } }); } public static void main(String[] args) { new GetBytecode().execute(args); } }
编译命令调整
新增utilities包的导出声明,完整编译命令如下:
javac --add-modules=jdk.hotspot.agent \ --add-exports "jdk.hotspot.agent/sun.jvm.hotspot.memory=ALL-UNNAMED" \ --add-exports "jdk.hotspot.agent/sun.jvm.hotspot.oops=ALL-UNNAMED" \ --add-exports "jdk.hotspot.agent/sun.jvm.hotspot.tools=ALL-UNNAMED" \ --add-exports "jdk.hotspot.agent/sun.jvm.hotspot.runtime=ALL-UNNAMED" \ --add-exports "jdk.hotspot.agent/sun.jvm.hotspot.classfile=ALL-UNNAMED" \ --add-exports "jdk.hotspot.agent/sun.jvm.hotspot.utilities=ALL-UNNAMED" \ *.java
跨版本兼容替代实现
如果需要代码同时兼容JDK8、JDK11+环境,可以直接通过堆遍历获取所有已加载类,避开allClassesDo的接口差异:
@Override public void run() { VM.getVM().getObjectHeap().iterate(heapObject -> { if (!(heapObject instanceof InstanceKlass)) { return; } InstanceKlass klass = (InstanceKlass) heapObject; if (!klass.getName().asString().equals("MyDebugger")) { return; } Method method = klass.findMethod("sendMessage", "()V"); if (method == null) { return; } for (byte bc : method.getByteCode()) { System.out.printf("%02x ", bc); } }); }
运行注意事项
- 运行SA工具的JDK版本必须和目标Java进程的JDK版本完全一致,否则会触发版本校验错误
- 运行时同样需要携带上述
--add-modules和--add-exports参数,否则会出现运行时权限错误
内容的提问来源于stack exchange,提问作者AntoineG
相关产品推荐
相关产品推荐

