JDK17下用JHSDB定位静态变量位置不符预期,求技术指导
JDK17下JHSDB未找到堆中静态变量staticObj的原因及解决办法
问题重现
运行以下Java代码后,按书籍步骤用JHSDB调试,未在堆中找到预期的staticObj静态变量:
package chapter9; public class JHSDBTest { static class Test{ static ObjectHolder staticObj = new ObjectHolder(); ObjectHolder instanceObj = new ObjectHolder(); void foo(){ ObjectHolder localObj = new ObjectHolder(); //debug System.out.println("done"); } } private static class ObjectHolder{} public static void main(String[] args) { Test test = new JHSDBTest.Test(); test.foo(); } }
已知JDK7后静态变量移出永久代,JDK8起元空间替代永久代,静态变量存于堆中,但JDK17环境下未找到目标对象,且已配置VM参数。
可能的原因及解决步骤
1. 查找方式错误:静态变量是类对象的一部分
JDK8+中,静态变量确实存在堆中,但它是作为Test类的Class对象的字段存储的,并非独立的顶级对象。你不能直接搜索staticObj这个对象,而要先找到Test的Class实例,再查看其字段引用的ObjectHolder对象:
- 用
jps获取进程ID,启动jhsdb hsdb --pid <进程ID> - 打开
Tools -> Class Browser,定位到chapter9.JHSDBTest$Test类 - 查看类的静态字段列表,找到
staticObj对应的对象地址 - 用
Tools -> Inspector输入该地址,即可看到对应的ObjectHolder实例,确认其在堆中
2. 对象被提前GC回收
如果程序运行过快,在你连接JHSDB前,对象可能被GC回收(虽然静态变量关联的类对象默认不会被轻易卸载,但极端情况可能发生):
- 修改
foo()方法,添加延迟让程序挂起:void foo(){ ObjectHolder localObj = new ObjectHolder(); System.out.println("done"); try { Thread.sleep(100000); // 挂起100秒,留足调试时间 } catch (InterruptedException e) { e.printStackTrace(); } } - 确保JHSDB连接时,程序处于运行状态,未进入退出流程
3. VM参数干扰
检查你配置的VM参数,是否存在影响类加载或堆内存的设置:
- 确保添加了JHSDB调试必需的参数:
-XX:+UnlockDiagnosticVMOptions -XX:+DebugNonSafepoints - 暂时移除自定义的GC参数或内存参数,用默认配置测试,排除参数干扰
- 若开启了
-XX:+ClassUnloadingWithConcurrentMark等类卸载参数,可能导致Test类被提前卸载,静态变量引用的对象也会被回收,建议关闭这类参数后重试
4. 对JHSDB工具的使用误解
部分旧书籍的JHSDB操作步骤可能基于低版本JDK,JDK17的JHSDB界面或命令有变化:
- 避免用旧的
find命令直接搜索对象,改用scanoop命令查找chapter9.JHSDBTest$ObjectHolder类型的所有实例,再结合类对象的字段引用判断哪个是staticObj - 示例命令(在JHSDB的CLI模式下):
会列出所有scanoop chapter9.JHSDBTest$ObjectHolderObjectHolder实例的地址,再通过inspectheap查看每个地址的归属,找到属于Test类静态字段的那个实例
内容的提问来源于stack exchange,提问作者竹玄羽
相关产品推荐
相关产品推荐

