应选 s.value.length;因 jdk 9+ 废弃 count 字段,string 改用 value(char[])且无 count,需用 s.value.length,或更稳妥的 s.value != null && s.value.length > 1000。

用 OQL 查 java.lang.String 时,s.count 和 s.value.length 到底该选哪个
在千万级堆快照里查“大字符串”,最常踩的坑是字段名失效——不是语法错,而是 JDK 版本导致的内部字段变更。s.count 在 JDK 7/8 中有效,但 JDK 9+ 的 String 已改用 value 字段(char[]),且不再暴露 count;此时必须用 s.value.length。jhat 的 OQL 虽做了一定兼容映射,但不保证跨版本一致。
实操建议:
- 先在 jhat 页面点开一个
java.lang.String实例,看其字段列表,确认实际存在的字段名(常见为value、value.length或coder) - 避免硬写
s.count > 1000,换成s.value != null && s.value.length > 1000更稳妥 - 如果查不到结果,试试
toString(s).length > 1000——它绕过字段访问,直接走对象 toString 行为,但性能略低
SELECT AS RETAINED SET 不是万能的,大对象保留集会拖慢查询
想定位泄漏源头,常会加 AS RETAINED SET 查某个类实例的完整保留集。但在千万级堆中,这个操作极易卡死或超时——因为 jhat 需为每个匹配对象递归计算支配树,内存和 CPU 开销呈指数增长。
实操建议:
- 先用轻量查询缩小范围:
SELECT * FROM com.example.User u WHERE u.id > 1000000,再对少量结果手动点开看 retained size - 若必须用保留集,优先配合
WHERE限制数量:SELECT AS RETAINED SET * FROM com.example.User u WHERE u.status = 'ACTIVE' LIMIT 50(注意:jhat 不支持 LIMIT,但 MAT 支持;如用 jhat,得靠前置过滤) - 真正要分析泄漏链,不如导出支配者视图(Dominator Tree)后,在 MAT 中用 OQL:
SELECT * FROM INSTANCEOF java.lang.Object o WHERE o.@retainedHeapSize > 1000000
正则匹配 toString(s) 很慢,LIKE 比 test() 更适合千万级扫描
查包含特定关键词的字符串时,有人写 /user_id/.test(s.value.toString()),这在百万级以下尚可,到千万级就容易让 jhat 崩溃——因为 test() 是 JavaScript 引擎执行,每条记录都要启动 JS 解析上下文。
实操建议:
- 改用 OQL 原生
LIKE操作符:SELECT s FROM java.lang.String s WHERE toString(s) LIKE '%user_id%',底层走字节匹配,快一个数量级 -
LIKE支持通配符:%(任意长度)、_(单字符),但不支持正则元字符(如.、*) - 若必须用正则,先用
LIKE粗筛,再把结果导出到本地用 Python 处理,别让 jhat 扛
查自定义实体类前,务必确认类加载器是否唯一
查 com.example.Order 时,如果返回为空,大概率不是没数据,而是该类被多个 ClassLoader 加载过(比如热部署、OSGi、Spring Boot DevTools)。OQL 默认只查“第一个”匹配的类,其余同名类被忽略。
实操建议:
- 先查所有加载器下的该类:
SELECT * FROM INSTANCEOF com.example.Order o(INSTANCEOF包含子类 + 所有 ClassLoader 实例) - 或者显式指定类地址:
SELECT * FROM 0x7a1b2c3d(从 jhat 的 “Classes” 页面复制对应类的 ID) - 更可靠的方式是结合类加载器信息:
SELECT o FROM com.example.Order o WHERE classof(o).classLoader != null,排除 bootstrap 类加载器干扰
千万级堆里跑 OQL,本质是拿时间换精度。jhat 的 OQL 引擎没有索引,全靠线性扫描+反射取值,任何带 outbounds()、dominators() 或深度嵌套字段访问的操作,都可能让查询从秒级变成小时级。真要高频分析,不如用 MAT 导入后建索引,或把关键字段提前 dump 成 CSV 再 grep。










