ReZero's Utopia.

OQL

Word count: 2.4kReading time: 10 min
2020/09/26 Share

JVM Object Query Language

参考链接

概述

简单说就是 java heap 信息的查询语言。
原本的小工具 hat 其实已经够用了,但是为了增加点灵活性,所以引入了 oql 方便查询。

1
2
3
select <JavaScript expression to select>
[ from [instanceof] <class name> <identifier>
[ where <JavaScript boolean expression to filter> ] ]

上面的表达式大致介绍下:

  • <class name> 标准Java类名(例如:java.net.URL)或数组类名。
    [C是字符数组名称,[Ljava.io.File;java.io.File []的名称,依此类推。
    要注意全限定描述的类名在运行时并不总是唯一地标识Java类,毕竟可能用着不一样的 classloader
    因此,只是允许将类名作为类对象的 identifier 字符串。

  • [instanceof] 可选,选了就连子类一同查,否则只查对应的类

  • selectwhere 后跟进的都是 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 对象

遍历

  1. heap.forEachClass - 为每个Java类调用一个回调函数
    heap.forEachClass(callback)

  2. heap.forEachObject - 为每个Java对象调用回调函数 clazz 就是选中的对象对应的类,includeSubtypes 就是指要不要包含子类
    heap.forEachObject(callback, [clazz = java.lang.Object], [includeSubtypes = true])

上面俩基本就简单的循环,没找到啥好应用,差不多好点的就是用来做个特殊计数啊之类的


查找

  1. heap.findClass(className) 根据 className 查出对应的 class 对象
  • 对象有以下的属性

    • name - 类的名称。

    • superclass - 超类的类对象(如果是java.lang.Object,则为null)。

    • statics - 类的静态字段的 <名称,值> 键值对集合。

    • fields - 字段对象的数组。field对象具有名称,签名属性。

    • loader - 加载此类的 ClassLoader 对象。

    • signers - 签署此类的签名者。

    • protectionDomain - 此类所属的保护域

  • 对象有以下的方法
    -isSubclassOf - 测试给定的类是否是此类的直接或间接子类。

    • isSuperclassOf - 测试给定的Class是否是此类的直接或间接超类。

    • subclasses - 返回直接和间接子类的数组。

    • superclasses - 返回直接和间接超类的数组。

  1. heap.findObject(stringIdOfObject) – 找实例

集合操作

  1. heap.classes – returns an enumeration of all Java classes

  2. heap.objects – 这个 filter 参数指的是在结果集的基础上用 filter 表达式做层过滤

    • heap.objects(clazz, [includeSubtypes=true], [filter])
  3. heap.finalizables – 返回等待 finalized 对象的枚举.

  4. heap.livepaths – 返回数组,数组里包含了存活的对象. 同时接一个次要参数,表明是否返回持有对象弱引用的。

    • heap.livepaths(id, [weakRef=false])
    • 返回对象的每个数组元素都是一个数组,这个子数组包含了在这个 引用链 路径上的对象
  5. 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)")

单个对象上的函数

  1. allocTrace 如果给定的Java对象可用,则返回分配栈跟踪。 具体是返回 帧对象的数组。每个帧对象具有以下属性:

    • className - 其方法在 frame 中运行的Java类的名称。
    • methodName - 运行的Java方法的名称。
    • methodSignature - frame 中运行的Java方法的签名。
    • sourceFileName - frame 中运行的Java类的源文件的名称。
    • lineNumber - 方法中的源行号。
  2. 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()

其他常用函数

  1. forEachReferrer 为给定Java对象的每个引用者调用一个回调函数。

  2. identical 返回两个给定的Java对象是否相同。

    • select identical(heap.findClass("Foo").statics.bar, heap.findClass("AnotherClass").statics.bar)
  3. objectid 返回给定Java对象的String id。此id可以传递给 heap.findObject,也可以用于比较对象以进行标识

    • select objectid(o) from java.lang.Object o
  4. reachables 返回从给定Java对象传递引用的Java对象数组。(可选)接受第二个参数,该参数是逗号分隔的字段名称,以从可达性计算中排除。字段以class_name.field_name模式编写。

    • select reachables(u, 'java.net.URL.handler') from java.net.URL u 打印每个java.net.URL中的所有可访问内容,但省略可通过指定字段访问的对象。
  5. referrers 返回引用了给定Java对象的所有对象

    • select u from java.net.URL u where count(referrers(u)) > 2 查询持有URL引用次数超过2的对象
  6. refers function, root function, sizeof function, toHtml function 这些听个名就了解大概了,不介绍了,感兴趣的话可以看文章开头的链接

多值筛选

展示每个线程的名称和线程信息

1
2
select { name: t.name? t.name.toString() : "null", thread: t } 
from instanceof java.lang.Thread t

迭代操作方法

这些方法接受 array/iterator/enumeration 和 一个表达式,用来对 集合对象迭代应用这些表达式

1
2
3
4
5
6
7
8
9
10
11
12
concat(array1/enumeration1, array2/enumeration2)
contains(array/enumeration, expression)
count(array/enumeration, expression)
filter(array/enumeration, expression)
length(array/enumeration)
map(array/enumeration, expression)
max(array/enumeration, [expression])
min(array/enumeration, [expression])
sort(array/enumeration, [expression])
sum(array/enumeration, [expression])
toArray(array/enumeration)
unique(array/enumeration, [expression])

三个迭代时使用的内置变量

  • it -> currently visited element
  • index -> index of the current element
  • array -> array/enumeration that is being iterated

contains 举个例子: 选择所有被某类静态字段引用的 Properties 对象。

1
2
select p from java.util.Properties p
where contains(referrers(p), "classof(it).name == 'java.lang.Class'")

用两个复杂的例子收个尾

1
2
3
select map(sort(map(heap.objects('java.lang.ClassLoader'), 
'{ loader: it, count: it.classes.elementCount }'), 'lhs.count < rhs.count'),
'toHtml(it) + "<br>"')
  1. java.lang.ClassLoader有一个名为classes的私有字段,类型为java.util.Vector。
  2. Vector有一个名为 elementCount的私有字段,是矢量中元素的数量。
  3. 我们使用JavaScript对象标识符 和map函数选择多个值(loader,count)。
  4. 然后带有比较表达式的排序函数对结果按计数(即加载的类数)进行排序。

Printing value of all System properties

1
2
3
4
5
6
7
8
9
10
select map(filter(heap.findClass('java.lang.System').statics.props.table, 'it != null'), 
function (it) {
var res = "";
while (it != null) {
res += it.key.value.toString() + '=' +
it.value.value.toString() + '<br>';
it = it.next;
}
return res;
});
  1. java.lang.System具有类型为java.util.Properties的名称为’props’的静态字段。
  2. java.util.Properties的字段为’table’,类型为java.util.Hashtable $ Entry(此字段继承自java.util.Hashtable)。这是hashtable桶数组。
  3. java.util.Hashtable $ Entry包含’key’,’value’和’next’字段。每个条目指向同一哈希表桶中的下一个条目(或null)。
  4. java.lang.String类具有char []类型的’value’字段。

tips

OQL 可能并不稳定–因为Java平台类的私有字段可能会在没有任何通知的情况下被修改/删除! (实现细节)。
但是,在用户类上使用这种查询可能是安全的–因为用户对类有控制权。

CATALOG
  1. 1. JVM Object Query Language
    1. 1.1. 概述
    2. 1.2. 整活
    3. 1.3. OQL 内置 对象,函数
      1. 1.3.1. 全局性的 heap 对象
        1. 1.3.1.1. 遍历
        2. 1.3.1.2. 查找
        3. 1.3.1.3. 集合操作
        4. 1.3.1.4. 举两个例子
      2. 1.3.2. 单个对象上的函数
        1. 1.3.2.1. 样例
        2. 1.3.2.2. 其他常用函数
      3. 1.3.3. 多值筛选
        1. 1.3.3.1. 迭代操作方法
      4. 1.3.4. 用两个复杂的例子收个尾
        1. 1.3.4.1. Print histogram of each class loader and number of classes loaded by it
        2. 1.3.4.2. Printing value of all System properties
        3. 1.3.4.3. tips