OQL 是一种类似于 SQL 的查询语言,用于查询 Java 堆。OQL 允许从 Java 堆中过滤/选择所需的信息。虽然 VisualVM 已经支持“显示类 X 的所有实例”等预定义查询,但 OQL 增加了更多灵活性。OQL是基于JavaScript的表达语言。
OQL 查询的形式如下:
select <JavaScript expression to select>
[ from [instanceof] <class name> <identifier>
[ where <JavaScript boolean expression to filter> ] ]
其中类名是完全限定的 Java 类名(例如:java.net.URL)或数组类名。char[](或[C)是char数组名称,java.io.File(或[Ljava.io.File;)是java.io.File[]的名称,依此类推。请注意,完全限定的类名并不总是在运行时唯一标识 Java 类。可能存在多个具有相同名称但由不同加载器加载的 Java 类。因此,类名允许是类对象的 id 字符串。如果使用instanceof关键字,则选择子类型对象。如果未指定此关键字,则仅选择指定的确切类的实例。from 和where子句都是可选的 。
在select和(可选)where子句中,表达式使用 JavaScript 表达式。Java 堆对象被包装为方便的脚本对象,以便可以以自然语法访问字段。例如,Java 字段可以使用 obj.field_name 语法访问,数组元素可以使用 array[index] 语法访问。每个选定的 Java 对象都绑定到from子句中指定的标识符名称的 JavaScript 变量。
OQL 示例
选择长度为 100 或以上的所有字符串
select s from java.lang.String s where s.count >= 100
选择长度为 256 或以上的所有 int 数组
select a from int[] a where a.length >= 256
显示与正则表达式匹配的字符串内容
select {instance: s, content: s.toString()} from java.lang.String s
where /java/.test(s.toString())
显示所有 File 对象的路径值
select file.path.toString() from java.io.File file
显示所有 ClassLoader 类的名称
select classof(cl).name
from instanceof java.lang.ClassLoader cl
显示由给定 id 字符串标识的类的实例
select o from instanceof 0xd404b198 o
请注意,0xd404b198 是一个类(在会话中)的 id。这是通过查看该类页面中显示的 id 找到的。
OQL 内置对象、函数
堆对象
堆内置对象支持以下方法:
heap.forEachClass——为每个Java类调用回调函数
heap.forEachClass(callback);
heap.forEachObject——为每个Java对象调用回调函数
heap.forEachObject(callback, clazz, includeSubtypes);
clazz是其实例被选择的类。如果未指定,则默认为 java.lang.Object。includeSubtypes是一个布尔标志,指定是否包含子类型实例。该标志的默认值为 true。
heap.findClass -- 查找给定名称的 Java 类
heap.findClass(className);
哪里className是要查找的类的名称。生成的 Class 对象具有以下属性:
名称 - 类的名称。
superclass - 超类的类对象(如果是 java.lang.Object,则为 null)。
statics - 类的静态字段的名称、值对。
fields - 字段对象的数组。字段对象具有名称、签名属性。
loader - 加载此类的 ClassLoader 对象。
类对象有以下方法:
isSubclassOf - 测试给定的类是否是此类的直接子类或间接子类。
isSuperclassOf - 测试给定的类是否是此类的直接或间接超类。
子类 - 返回直接和间接子类的数组。
超类 - 返回直接和间接超类的数组。
heap.findObject -- 从给定的对象 ID 中查找对象
heap.findObject(stringIdOfObject);
heap.classes -- 返回所有 Java 类的枚举
heap.objects -- 返回 Java 对象的枚举
heap.objects(clazz, [includeSubtypes], [filter])
clazz是其实例被选择的类。如果未指定,则默认为 java.lang.Object。includeSubtypes是一个布尔标志,指定是否包含子类型实例。该标志的默认值为 true。此方法接受一个可选的过滤表达式来过滤对象的结果集。
heap.finalizeds —— 返回等待最终确定的 Java 对象的枚举。
heap.livepaths -- 返回给定对象处于活动状态的路径数组。此方法接受可选的第二个参数,它是一个布尔标志。该标志指示是否包含具有弱引用的路径。默认情况下,不包括具有弱引用的路径。
select heap.livepaths(s) from java.lang.String s
该数组本身的每个元素都是另一个数组。后面的数组包含路径“引用链”中的对象。
heap.roots——返回堆根的枚举。 每个 Root 对象都具有以下属性:
id - 此根引用的对象的字符串 id
type - 根的描述类型(JNI Global、JNI Local、Java Static 等)
描述 - 根的字符串描述
Referrer - 负责此根的线程对象或类对象或 null
例子:
访问类 java.lang.System 的静态字段“props”
select heap.findClass("java.lang.System").statics.props
select heap.findClass("java.lang.System").props
获取 java.lang.String 类的字段数
select heap.findClass("java.lang.String").fields.length
查找给定对象 id 的对象
select heap.findObject("0xf3800b58")
选择具有名称模式 java.net.* 的所有类
select filter(heap.classes(), "/java.net./.test(it.name)")
单个对象上的函数
allocTrace(作业)
类(作业对象)
forEachReferrer(回调,jobobject)
相同(o1,o2)
对象ID(作业对象)
可达(作业,排除字段)
引用者(作业)
裁判员(工作对象)
引用(作业)
根(作业)
大小(作业对象)
rsizeof(作业对象)
toHtml(对象)
alloctrace 函数
这将返回给定 Java 对象的分配站点跟踪(如果可用)。allocTrace 返回帧对象数组。每个框架对象都具有以下属性:
className - 其方法在框架中运行的 Java 类的名称。
methodName - 框架中运行的 Java 方法的名称。
methodSignature - 框架中运行的 Java 方法的签名。
sourceFileName - 框架中运行的 Java 类的源文件的名称。
lineNumber - 方法内的源行号。
函数类别
返回给定 Java 对象的类对象。结果对象支持以下属性:
名称 - 类的名称。
superclass - 超类的类对象(如果是 java.lang.Object,则为 null)。
statics - 类的静态字段的名称、值对。
fields - 字段对象的数组。字段对象具有名称、签名属性。
loader - 加载此类的 ClassLoader 对象。
类对象有以下方法:
isSubclassOf - 测试给定的类是否是此类的直接子类或间接子类。
isSuperclassOf - 测试给定的类是否是此类的直接或间接超类。
子类 - 返回直接和间接子类的数组。
超类 - 返回直接和间接超类的数组。
例子:
显示每个引用类型对象的类名
select classof(o).name from instanceof java.lang.ref.Reference o
显示 java.io.InputStream 的所有子类
select heap.findClass("java.io.InputStream").subclasses()
显示 java.io.BufferedInputStream 的所有超类
select heap.findClass("java.io.BufferedInputStream").superclasses()
forEachReferrer 函数
为给定 Java 对象的每个引用者调用回调函数。
相同的功能
返回两个给定的 Java 对象是否相同。
例子:
select identical(heap.findClass("Foo").statics.bar, heap.findClass("AnotherClass").statics.bar)
对象ID函数
返回给定 Java 对象的字符串 id。该 id 可以传递给 heap.findObject,也可以用于比较对象的身份。
例子:
select objectid(o) from java.lang.Object o
可达函数
返回从给定 Java 对象传递引用的 Java 对象数组。可以选择接受第二个参数,该参数是要从可达性计算中排除的逗号分隔字段名称。字段以 class_name.field_name 模式编写。
例子:
打印每个 Properties 实例的所有可访问对象。
select reachables(p) from java.util.Properties p
打印每个 java.net.URL 的所有可访问对象,但忽略通过指定字段可访问的对象。
select reachables(u, 'java.net.URL.handler') from java.net.URL u
推荐人功能
返回保存对给定 Java 对象的引用的 Java 对象的枚举。
例子:
打印每个 java.lang.Object 实例的引用者数量
select count(referrers(o)) from java.lang.Object o
打印每个 java.io.File 对象的引荐来源网址
select referrers(f) from java.io.File f
仅当被 2 个或更多引用时才打印 URL 对象
select u from java.net.URL u where count(referrers(u)) > 2
裁判功能
返回给定 Java 对象直接引用的 Java 对象数组。
示例:打印 java.io.File 类的所有静态引用字段
select referees(heap.findClass("java.io.File"))
引用函数
返回第一个 Java 对象是否引用第二个 Java 对象。
根函数
如果给定对象是对象根集的成员,则此函数返回一个描述性Root 对象,描述其原因。如果给定对象不是根,则此函数返回 null。
函数的大小
返回给定 Java 对象的大小(以字节为单位)示例:
select sizeof(o) from int[] o
保留大小函数
返回给定 Java 对象的保留集的大小(以字节为单位) 警告!首次在堆转储上使用此函数可能会花费大量时间 示例:
select rsizeof(o) from instanceof java.lang.HashMap o
toHtml 函数
返回给定 Java 对象的 HTML 字符串。请注意,对于选择表达式选择的对象,这是自动调用的。但是,打印更复杂的输出可能很有用。示例:以粗体打印超链接
select "<b>" + toHtml(o) + "</b>" from java.lang.Object o
选择多个值
可以使用 JavaScript 对象文字或数组选择多个值。
示例:显示每个线程对象的名称和线程
select { name: t.name? t.name.toString() : "null", thread: t }
from instanceof java.lang.Thread t
数组/迭代器/枚举操作函数
这些函数接受数组/迭代器/枚举和表达式字符串[或回调函数]作为输入。这些函数迭代数组/迭代器/枚举并将表达式(或函数)应用于每个元素。请注意,JavaScript 对象是关联数组。因此,这些函数也可以与任意 JavaScript 对象一起使用。
concat(数组1/枚举1,数组2/枚举2)
包含(数组/枚举,表达式)
计数(数组/枚举、表达式)
过滤器(数组/枚举、表达式)
长度(数组/枚举)
映射(数组/枚举、表达式)
max(数组/枚举, [表达式])
min(数组/枚举, [表达式])
排序(数组/枚举,[表达式])
top(数组/枚举, [表达式], [顶部])
sum(数组/枚举, [表达式])
toArray(数组/枚举)
唯一(数组/枚举,[表达式])
连接函数
连接两个数组或枚举(即返回复合枚举)。
包含函数
返回给定数组/枚举是否包含代码中指定的给定布尔表达式的元素。评估的代码可以引用以下内置变量。
it -> 当前访问的元素
index -> 当前元素的索引
array -> 正在迭代的数组/枚举
示例:选择某个类的某个静态字段引用的所有 Properties 对象。
select p from java.util.Properties p
where contains(referrers(p), "classof(it).name == 'java.lang.Class'")
计数功能
count 函数返回输入数组/枚举中满足给定布尔表达式的元素的计数。布尔表达式代码可以引用以下内置变量。
it -> 当前访问的元素
index -> 当前元素的索引
array -> 正在迭代的数组/枚举
示例:打印具有特定名称模式的类的数量
select count(heap.classes(), "/java.io./.test(it.name)")
过滤功能
filter 函数返回一个数组/枚举,其中包含满足给定布尔表达式的输入数组/枚举的元素。布尔表达式代码可以引用以下内置变量。
it -> 当前访问的元素
index -> 当前元素的索引
array -> 正在迭代的数组/枚举
结果 -> 结果数组/枚举
例子:
显示具有 java.io.* 名称模式的所有类
select filter(heap.classes(), "/java.io./.test(it.name)")
显示 URL 对象的所有引用者,其中引用者不是来自 java.net 包
select filter(referrers(u), "! /java.net./.test(classof(it).name)")
from java.net.URL u
长度函数
length 函数返回数组/枚举的元素数量。
地图功能
通过评估每个元素上的给定代码来转换给定的数组/枚举。评估的代码可以引用以下内置变量。
it -> 当前访问的元素
index -> 当前元素的索引
array -> 正在迭代的数组/枚举
结果 -> 结果数组/枚举
map 函数返回通过在输入数组/枚举的每个元素上重复调用代码而创建的值的数组/枚举。
示例:显示 java.io.File 的所有静态字段及其名称和值
select map(heap.findClass("java.io.File").statics, "index + '=' + toHtml(it)")
最大函数
返回给定数组/枚举的最大元素。可以选择接受代码表达式来比较数组的元素。默认情况下使用数值比较。比较表达式可以使用以下内置变量:
lhs -> 用于比较的左侧元素
rhs -> 用于比较的右侧元素
例子:
找到任意 String 实例的最大长度
select max(map(heap.objects('java.lang.String', false), 'it.count'))
查找具有最大长度的字符串实例
select max(heap.objects('java.lang.String'), 'lhs.count > rhs.count')
最小函数
返回给定数组/枚举的最小元素。可以选择接受代码表达式来比较数组的元素。默认情况下使用数值比较。比较表达式可以使用以下内置变量:
lhs -> 用于比较的左侧元素
rhs -> 用于比较的右侧元素
例子:
求任意 Vector 实例的最小尺寸
select min(map(heap.objects('java.util.Vector', false), 'it.elementData.length'))
查找具有最大长度的 Vector 实例
select min(heap.objects('java.util.Vector'), 'lhs.elementData.length < rhs.elementData.length')
排序功能
对给定数组/枚举进行排序。可以选择接受代码表达式来比较数组的元素。默认情况下使用数值比较。比较表达式可以使用以下内置变量:
lhs -> 用于比较的左侧元素
rhs -> 用于比较的右侧元素
例子:
按大小顺序打印所有 char[] 对象。
select sort(heap.objects('char[]'), 'sizeof(lhs) - sizeof(rhs)')
按大小顺序打印所有 char[] 对象,但也打印大小。
select map(sort(heap.objects('char[]'), 'sizeof(lhs) - sizeof(rhs)'), '{ size: sizeof(it), obj: it }')
顶部功能
返回给定数组/枚举的前 N 个元素。可选择接受代码表达式来比较数组的元素和顶部元素的数量。默认情况下,返回按出现顺序排列的前 10 个元素。比较表达式可以使用以下内置变量:
lhs -> 用于比较的左侧元素
rhs -> 用于比较的右侧元素
例子:
打印 5 个最长的字符串
select top(heap.objects('java.lang.String'), 'rhs.count - lhs.count', 5)
打印 5 个最长的字符串,同时也打印大小。
select map(top(heap.objects('java.lang.String'), 'rhs.count - lhs.count', 5), '{ length: it.count, obj: it }')
求和函数
此函数返回给定输入数组或枚举的所有元素的总和。(可选)接受表达式作为第二个参数。这用于在求和之前映射输入元素。
示例:返回每个 Properties 对象的可到达对象的大小总和
select sum(map(reachables(p), 'sizeof(it)'))
from java.util.Properties p
// or omit the map as in ...
select sum(reachables(p), 'sizeof(it)')
from java.util.Properties p
toArray 函数
此函数返回一个包含输入数组/枚举元素的数组。
独特的功能
此函数返回一个数组/枚举,其中包含给定输入数组/枚举的唯一元素
示例:选择从字符串引用的唯一 char[] 实例。请注意,多个 String 实例可以共享相同的 char[] 内容。
// number of unique char[] instances referenced from any String
select count(unique(map(heap.objects('java.lang.String'), 'it.value')))
// total number of Strings
select count(heap.objects('java.lang.String'))
更复杂的例子
打印每个类加载器的直方图及其加载的类的数量
select map(sort(map(heap.objects('java.lang.ClassLoader'),
'{ loader: it, count: it.classes.elementCount }'), 'rhs.count - lhs.count'),
'toHtml(it) + "
"')
上面的查询使用了以下事实:java.lang.ClassLoader有一个名为java.util.Vector类型的类的私有字段,并且 Vector 有一个名为elementCount的私有字段,它表示向量中的元素数量。我们使用 JavaScript 对象文字和映射函数选择多个值(加载器、计数)。我们使用带有比较表达式的排序函数按计数(即加载的类数量)对结果进行排序。
显示每个类加载器实例的父子链
select map(heap.objects('java.lang.ClassLoader'),
function (it) {
var res = '';
while (it != null) {
res += toHtml(it) + "->";
it = it.parent;
}
res += "null";
return res + "
";
})
请注意,我们使用java.lang.ClassLoader类的parent字段,并使用回调函数进行映射调用,直到parent 为null。
打印所有系统属性的值
select map(filter(heap.findClass('java.lang.System').props.table, 'it != null && it.key != null && it.value != null'),
function (it) {
var res = it.key.toString() + ' = ' + it.value.toString();
return res;
});
上述查询使用以下事实:
java.lang.System 有一个名为“props”、类型为 java.util.Properties 的静态字段。
java.util.Properties 具有 java.util.Hashtable$Entry 类型的“table”字段(该字段继承自 java.util.Hashtable)。这是哈希表桶数组。
java.util.Hashtable$Entry 具有“key”、“value”和“next”字段。每个条目都指向同一哈希表桶中的下一个条目(或空)。
java.lang.String 类具有 char[] 类型的“value”字段。
请注意,此查询(以及许多其他查询)可能不稳定 - 因为 Java 平台类的私有字段可能会在没有任何通知的情况下被修改/删除!(实施细节)。但是,在用户类上使用此类查询可能是安全的 - 假设用户可以控制类。