OQL
JVM Object Query Language
概述
简单说就是 java heap 信息的查询语言。
原本的小工具 hat 其实已经够用了,但是为了增加点灵活性,所以引入了 oql 方便查询。
| 1 | select <JavaScript expression to select> | 
上面的表达式大致介绍下:
- <class name>标准Java类名(例如:- java.net.URL)或数组类名。- [C是字符数组名称,- [Ljava.io.File;是- java.io.File []的名称,依此类推。
 要注意全限定描述的类名在运行时并不总是唯一地标识Java类,毕竟可能用着不一样的- classloader。
 因此,只是允许将类名作为类对象的- identifier字符串。
- [instanceof]可选,选了就连子类一同查,否则只查对应的类
- select和- where后跟进的都是- js表达式作为子句。
 这么搞是为了方便,- js作为动态语言用起来算是比较爽的了,比如- object.filed_name,- arr[index],用个正则啊之类的。
 然后跟- sql差不多,就是- from里给个别名,然后- select里用。通过这种方式把- from里的- id对应的- object绑到- js变量上。
整活
- select all Strings of length 100 or more // 查询超过 100 长度的字符串 - select s from java.lang.String s where s.value.length >= 100
- select all int arrays of length 256 or more // 查询超过 256 长度的数组 - select a from [I a where a.length >= 256
- show content of Strings that match a regular expression // 字符串对象匹配正则 - select s.value.toString() from java.lang.String s where /java/.test(s.value.toString())
- show path value of all File objects // 是不是开始 get 爽点了? - select file.path.value.toString() from java.io.File file
- show names of all ClassLoader classes // 这个应该算是真正的唯一标识了吧 - select classof(cl).name from instanceof java.lang.ClassLoader cl
- show instances of the Class identified by given id string // 这个 id 就是 jhat 的id(如下图), 或者后文中的objectId函数也可以拿到 - select o from instanceof 0xd404b198 o
Note that 0xd404b198 is id of a Class (in a session). This is found by looking at the id shown in that class’s page.

OQL 内置 对象,函数
全局性的 heap 对象
遍历
- heap.forEachClass - 为每个Java类调用一个回调函数 - heap.forEachClass(callback)
- heap.forEachObject - 为每个Java对象调用回调函数 - clazz就是选中的对象对应的类,- includeSubtypes就是指要不要包含子类- heap.forEachObject(callback, [clazz = java.lang.Object], [includeSubtypes = true])
上面俩基本就简单的循环,没找到啥好应用,差不多好点的就是用来做个特殊计数啊之类的
查找
- heap.findClass(className)根据 className 查出对应的 class 对象
- 对象有以下的属性 - name - 类的名称。 
- superclass - 超类的类对象(如果是java.lang.Object,则为null)。 
- statics - 类的静态字段的 - <名称,值>键值对集合。
- fields - 字段对象的数组。field对象具有名称,签名属性。 
- loader - 加载此类的 - ClassLoader对象。
- signers - 签署此类的签名者。 
- protectionDomain - 此类所属的保护域 
 
- 对象有以下的方法 
 -isSubclassOf - 测试给定的类是否是此类的直接或间接子类。- isSuperclassOf - 测试给定的Class是否是此类的直接或间接超类。 
- subclasses - 返回直接和间接子类的数组。 
- superclasses - 返回直接和间接超类的数组。 
 
- heap.findObject(stringIdOfObject)– 找实例
集合操作
- heap.classes – returns an enumeration of all Java classes 
- heap.objects – 这个 filter 参数指的是在结果集的基础上用 filter 表达式做层过滤 
- heap.objects(clazz, [includeSubtypes=true], [filter])
- heap.finalizables – 返回等待 - finalized对象的枚举.
- heap.livepaths – 返回数组,数组里包含了存活的对象. 同时接一个次要参数,表明是否返回持有对象弱引用的。 
- heap.livepaths(id, [weakRef=false])
- 返回对象的每个数组元素都是一个数组,这个子数组包含了在这个 引用链 路径上的对象
- heap.roots – 返回根对象- id 此根引用的对象的字符串id
- type 描述类型的 Root (JNI Global, JNI Local, Java Static etc)
- description Root的字符串描述
- referrer - 负责此根或null的Thread Object或Class对象
 
举两个例子
access static field ‘props’ of class java.lang.System // 找出 System 类 static 字段 props 的相关信息
select heap.findClass("java.lang.System").statics.props
get number of fields of java.lang.String class // 找出 String 类的 字段信息
select heap.findClass("java.lang.String").fields.length
find the object whose object id is given // 楞找
select heap.findObject("0xf3800b58")
select all classes that have name pattern java.net.* // 过滤,看到这感觉 oql 的 api 设计的不够简练,似乎有些冗余 api
select filter(heap.classes(), "/java.net./.test(it.name)")
单个对象上的函数
- allocTrace 如果给定的Java对象可用,则返回分配栈跟踪。 具体是返回 帧对象的数组。每个帧对象具有以下属性:
- className - 其方法在 frame 中运行的Java类的名称。
- methodName - 运行的Java方法的名称。
- methodSignature - frame 中运行的Java方法的签名。
- sourceFileName - frame 中运行的Java类的源文件的名称。
- lineNumber - 方法中的源行号。
- classof 返回给定Java对象的Class对象。结果对象支持以下属性,和 heap.object 的那个差不多
- 属性 - name - 类的名称。
- superclass - 超类的类对象(如果是java.lang.Object,则为null)。
- statics - 类的静态字段的名称,值对。
- fields - 字段对象的数组。字段对象具有名称,签名属性。
- loader - 加载此类的ClassLoader对象。
- signers - 签署此类的签名者。
- protectionDomain - 此类所属的保护域。
 
- 方法 - isSubclassOf - 测试给定的类是否是此类的直接或间接子类。
- isSuperclassOf - 测试给定的Class是否是此类的直接或间接超类。
- subclasses - 返回直接和间接子类的数组。
- superclasses - 返回直接和间接超类的数组。
 
样例
- show class name of each Reference type object // - select classof(o).name from instanceof java.lang.ref.Reference o
- show all subclasses of java.io.InputStream // inpustream 的子类 - select heap.findClass("java.io.InputStream").subclasses()
- show all superclasses of java.io.BufferedInputStream // bufferedInputStream 的父类 - select heap.findClass("java.io.BufferedInputStream").superclasses()
其他常用函数
- forEachReferrer为给定Java对象的每个引用者调用一个回调函数。
- identical返回两个给定的Java对象是否相同。- select identical(heap.findClass("Foo").statics.bar, heap.findClass("AnotherClass").statics.bar)
 
- objectid返回给定Java对象的String id。此id可以传递给 heap.findObject,也可以用于比较对象以进行标识- select objectid(o) from java.lang.Object o
 
- reachables返回从给定Java对象传递引用的Java对象数组。(可选)接受第二个参数,该参数是逗号分隔的字段名称,以从可达性计算中排除。字段以class_name.field_name模式编写。- select reachables(u, 'java.net.URL.handler') from java.net.URL u打印每个java.net.URL中的所有可访问内容,但省略可通过指定字段访问的对象。
 
- referrers返回引用了给定Java对象的所有对象- select u from java.net.URL u where count(referrers(u)) > 2查询持有URL引用次数超过2的对象
 
- refers function, root function, sizeof function, toHtml function 这些听个名就了解大概了,不介绍了,感兴趣的话可以看文章开头的链接 
多值筛选
展示每个线程的名称和线程信息
| 1 | select { name: t.name? t.name.toString() : "null", thread: t } | 
迭代操作方法
这些方法接受 array/iterator/enumeration 和 一个表达式,用来对 集合对象迭代应用这些表达式
| 1 | concat(array1/enumeration1, array2/enumeration2) | 
三个迭代时使用的内置变量
- it -> currently visited element
- index -> index of the current element
- array -> array/enumeration that is being iterated
用 contains 举个例子: 选择所有被某类静态字段引用的 Properties 对象。
| 1 | select p from java.util.Properties p | 
用两个复杂的例子收个尾
Print histogram of each class loader and number of classes loaded by it
| 1 | select map(sort(map(heap.objects('java.lang.ClassLoader'), | 
- java.lang.ClassLoader有一个名为classes的私有字段,类型为java.util.Vector。
- Vector有一个名为 elementCount的私有字段,是矢量中元素的数量。
- 我们使用JavaScript对象标识符 和map函数选择多个值(loader,count)。
- 然后带有比较表达式的排序函数对结果按计数(即加载的类数)进行排序。
Printing value of all System properties
| 1 | select map(filter(heap.findClass('java.lang.System').statics.props.table, 'it != null'), | 
- java.lang.System具有类型为java.util.Properties的名称为’props’的静态字段。
- java.util.Properties的字段为’table’,类型为java.util.Hashtable $ Entry(此字段继承自java.util.Hashtable)。这是hashtable桶数组。
- java.util.Hashtable $ Entry包含’key’,’value’和’next’字段。每个条目指向同一哈希表桶中的下一个条目(或null)。
- java.lang.String类具有char []类型的’value’字段。
tips
OQL 可能并不稳定–因为Java平台类的私有字段可能会在没有任何通知的情况下被修改/删除! (实现细节)。
但是,在用户类上使用这种查询可能是安全的–因为用户对类有控制权。
- Title: OQL
- Author: ReZero
- Created at : 2020-09-26 10:10:00
- Updated at : 2025-05-26 05:53:22
- Link: https://rezeros.github.io/2020/09/26/oql/
- License: This work is licensed under CC BY-NC-SA 4.0.