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平台类的私有字段可能会在没有任何通知的情况下被修改/删除! (实现细节)。
但是,在用户类上使用这种查询可能是安全的–因为用户对类有控制权。
- Post title:OQL
- Post author:ReZero
- Create time:2020-09-26 10:10:00
- Post link:https://rezeros.github.io/2020/09/26/oql/
- Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.