当前位置: 移动技术网 > IT编程>开发语言>Java > JVM 对象查询语言(OQL)[转载]

JVM 对象查询语言(OQL)[转载]

2019年09月24日  | 移动技术网IT编程  | 我要评论

219.144.206.60,浏览器自动关闭,川fa8512

最近生产环境出现一个很奇怪的问题,测试环境无法重现,本地直连生产无法重现。于是用上 jmap + java visualvm 的 oql (object query language) 分析问题。

关于ogl的文章不多,特此转载,原文出处:

 

本文主要翻译自jdk 1.8的jvm监控工具jhat中关于oql的英文帮助说明。

可以在jhat 和 jvisualvm 中进行实践。

oql(对象查询语言) 

oql是用于查询java堆的类sql查询语言。oql允许过滤/选择从java堆中获取的信息。虽然hat已经支持预定义的查询,例如“显示类x的所有实例”,但oql增加了更多的灵活性。oql基于javascript表达式语言。
 

oql查询的形式

select <javascript expression to select>
[ from [instanceof] <class name> <identifier>
[ where <javascript boolean expression to filter> ] ]

解释: 
(1)class name是java类的完全限定名,如:java.lang.string, java.util.arraylist, [c是char数组, [ljava.io.file是java.io.file[],依此类推
(2)类的完全限定名不足以唯一的辨识一个类,因为不同的classloader载入的相同的类,它们在jvm中是不同类型的
(3)instanceof表示也查询某一个类的子类,如果不明确instanceof,则只精确查询class name指定的类
(4)from和where子句都是可选的
(5)可以使用obj.field_name语法访问java字段,并且可以使用array [index]语法访问数组元素
 

oql示例

  • 查询长度大于等于100的字符串
    select s from java.lang.string s where s.value.length >= 100
  • 查询长度大于等于256的int数组
    select a from [i a where a.length >= 256

        另一种方式:select a from int[] a where a.length >= 256

  • 显示与正则表达式匹配的字符串的内容
    1.  
      select s.value.tostring() from java.lang.string s
    2.  
      where /java/.test(s.value.tostring())

        /java/ 修改成你的正则表达式,如/^myclass$/ 就会匹配myclass这个字符串

  • 显示所有file对象的文件路径
    select file.path.value.tostring() from java.io.file file

     

  • 显示所有classloader类的名称
    select classof(cl).name from instanceof java.lang.classloader cl
  • 显示由给定id字符串标识的class的实例
    select o from instanceof 0x741012748 o
    
    请注意,0x741012748是类的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);
    
    where classname是要查找的类的名称。生成的class对象具有以下属性:
    • name - 类的名称。
    • superclass - 超类的类对象(如果是java.lang.object,则为null)。
    • statics - 类的静态字段的名称,值对。
    • fields - 字段对象的数组。field对象具有名称,签名属性。
    • loader - 加载此类的classloader对象。
    • signers - 签署此类的签名者。
    • protectiondomain - 此类所属的保护域。
    类对象具有以下方法:
    • issubclassof - 测试给定的类是否是此类的直接或间接子类。
    • issuperclassof - 测试给定的class是否是此类的直接或间接超类。
    • subclasses - 返回直接和间接子类的数组。
    • superclasses - 返回直接和间接超类的数组。
  • heap.findobject - 从给定的对象id中查找对象
    heap.findobject(stringidofobject);
    
  • heap.classes - 返回所有java类的枚举
  • heap.objects - 返回java对象的枚举
    heap.objects(clazz, [includesubtypes], [filter])
    
    clazz是选择其实例的类。如果未指定,则默认为java.lang.object。includesubtypes是一个布尔标志,指定是否包含子类型实例。该标志的默认值为true。此方法接受可选的过滤器表达式以过滤对象的结果集。
  • heap.finalizables - 返回待完成的java对象的枚举。
  • heap.livepaths - 返回给定对象存活的路径数组。此方法接受可选的第二个参数,它是一个布尔标志。此标志指示是否包含弱引用的路径。默认情况下,不包括具有弱引用的路径。
    select heap.livepaths(s) from java.lang.string s
    
    该数组本身的每个元素都是另一个数组。后一个数组包含一个位于路径“引用链”中的对象。
  • heap.roots - 返回堆的根的枚举。 每个root对象都具有以下属性:
    • id - 此根引用的对象的字符串id
    • type - 描述类型的root(jni global,jni local,java static等)
    • description - root的字符串描述
    • referrer - 负责此根或null的thread object或class对象

例子:

  • 访问类java.lang.system的静态字段'props'
    select heap.findclass("java.lang.system").statics.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(jobject)
  • classof(jobject)
  • foreachreferrer(callback, jobject)
  • identical(o1, o2)
  • objectid(jobject)
  • reachables(jobject, excludedfields)
  • referrers(jobject)
  • referees(jobject)
  • refers(jobject)
  • root(jobject)
  • sizeof(jobject)
  • tohtml(obj)

 

alloctrace函数

这将返回给定java对象的分配站点跟踪(如果可用)。alloctrace返回对象的数组。每个对象具有以下属性:

  • classname - 其方法在框架中运行的java类的名称。
  • methodname - 运行的java方法的名称。
  • methodsignature - 框架中运行的java方法的签名。
  • sourcefilename - 框架中运行的java类的源文件的名称。
  • linenumber - 方法中的源行号。

classof函数

返回给定java对象的class对象。结果对象支持以下属性:

  • name - 类的名称。
  • superclass - 超类的类对象(如果是java.lang.object,则为null)。
  • 静态 - 类的静态字段的名称,值对。
  • fields - 字段对象的数组。字段对象具有名称,签名属性。
  • loader - 加载此类的classloader对象。
  • 签名者 - 签署此类的签名者。
  • protectiondomain - 此类所属的保护域。

类对象具有以下方法:

  • issubclassof - 测试给定的类是否是此类的直接或间接子类。
  • issuperclassof - 测试给定的class是否是此类的直接或间接超类。
  • subclasses - 返回直接和间接子类的数组。
  • superclasses - 返回直接和间接超类的数组。

例子:

  • 显示每个reference类型对象的类名
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对象的每个引用者调用一个回调函数。

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模式编写。

例子:

  • 从每个properties实例打印所有可到达的对象。
select reachables(p) from java.util.properties p
  • 打印每个java.net.url中的所有可访问内容,但省略可通过指定字段访问的对象。
  1.  
    select reachables(u, 'java.net.url.handler') from java.net.url u
  2.  
     

referrers函数

返回引用了给定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

referees函数

返回给定java对象直接引用的java对象数组。

示例:打印java.io.file类的所有静态引用字段

select referees(heap.findclass("java.io.file"))

refers函数

返回第一个java对象是否引用第二个java对象。

root函数

如果给定对象是根对象集的成员,则此函数返回描述其原因的描述性根对象。如果给定的对象不是root,则此函数返回null。

sizeof函数

以字节为单位返回给定java对象的大小示例:

 select sizeof(o) from [i o

tohtml函数

返回给定java对象的html字符串。请注意,对于select表达式选择的对象,会自动调用此方法。但是,打印更复杂的输出可能很有用。示例:以粗体字体重量打印超链接

select "<b>" + tohtml(o) + "</b>" from java.lang.object o

 

选择多个值

可以使用javascript对象文字或数组选择多个值。

示例:显示每个线程对象的名称和线程

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

 

数组/迭代器/枚举操作函数

这些函数接受数组/迭代器/枚举和表达式字符串[或回调函数]作为输入。这些函数迭代数组/迭代器/枚举,并在每个元素上应用表达式(或函数)。请注意,javascript对象是关联数组。因此,这些函数也可以与任意javascript对象一起使用。

concat函数

连接两个数组或枚举(即返回复合枚举)。

contains函数

返回给定的数组/枚举是否包含代码中指定的给定布尔表达式的元素。评估的代码可以引用以下内置变量。

  • it - >目前访问过的元素
  • index - >当前元素的索引
  • array - >正在迭代的数组/枚举

示例:选择某些静态字段引用某些类的所有properties对象。

  1.  
    select p from java.util.properties p
  2.  
    where contains(referrers(p), "classof(it).name == 'java.lang.class'")
  • 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])

count函数

count函数返回满足给定布尔表达式的输入数组/枚举的元素数。布尔表达式代码可以引用以下内置变量。

  • it - >目前访问过的元素
  • index - >当前元素的索引
  • array - >正在迭代的数组/枚举

示例:查询匹配特定名称模式的类的数量

select count(heap.classes(), "/java.io./.test(it.name)")

filter函数

filter函数返回一个数组/枚举,其中包含满足给定布尔表达式的输入数组/枚举的元素。布尔表达式代码可以引用以下内置变量。

  • it - >目前访问过的元素
  • index - >当前元素的索引
  • array - >正在迭代的数组/枚举
  • result - > result array / enumeration

例子:

  • 显示所有具有匹配java.io. * 的类
    select filter(heap.classes(), "/java.io./.test(it.name)")
  • 显示引用者不是来自java.net包的url对象的所有引用
    1.  
      select filter(referrers(u), "! /java.net./.test(classof(it).name)")
    2.  
      from java.net.url u

     

length函数

length函数返回数组/枚举的元素数。

map函数

通过评估每个元素上的给定代码来转换给定的数组/枚举。评估的代码可以引用以下内置变量。

  • it - >目前访问过的元素
  • index - >当前元素的索引
  • array - >正在迭代的数组/枚举
  • result - > result array / enumeration

map函数返回通过在输入数组/枚举的每个元素上重复调用代码而创建的值的数组/枚举。

示例:显示具有名称和值的java.io.file的所有静态字段

select map(heap.findclass("java.io.file").statics, "index + '=' + tohtml(it)")

max函数

返回给定数组/枚举的最大元素。(可选)接受代码表达式以比较数组的元素。默认情况下使用数字比较。比较表达式可以使用以下内置变量:

  • lhs - >左侧元素进行比较
  • rhs - >右侧元素进行比较

例子:

  • 找到任何string实例的最大长度
    select max(map(heap.objects('java.lang.string', false), 'it.value.length'))
    
  • 查找具有最大长度的字符串实例
    1.  
      select max(heap.objects('java.lang.string'), 'lhs.value.length > rhs.value.length')
    2.  
       

min函数

返回给定数组/枚举的最小元素。(可选)接受代码表达式以比较数组的元素。默认情况下使用数字比较。比较表达式可以使用以下内置变量:

  • 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')
    

sort函数

给出数组/枚举的排序。(可选)接受代码表达式以比较数组的元素。默认情况下使用数字比较。比较表达式可以使用以下内置变量:

  • lhs - >左侧元素进行比较
  • rhs - >右侧元素进行比较

例子:

  • 按大小顺序打印所有char []对象。
select sort(heap.objects('[c'), 'sizeof(lhs) - sizeof(rhs)')
  • 按大小顺序打印所有char []对象,同时也打印大小。
select map(sort(heap.objects('[c'), 'sizeof(lhs) - sizeof(rhs)'), '{ size: sizeof(it), obj: it }')

sum函数

此函数返回给定输入数组或枚举的所有元素的总和。(可选)接受表达式作为第二个参数。这用于在对输入元素求和之前映射输入元素。

示例:返回每个properties对象中可到达对象的大小总和

  1.  
    select sum(map(reachables(p), 'sizeof(it)'))
  2.  
    from java.util.properties p
  3.  
     
  4.  
    // or omit the map as in ...
  5.  
    select sum(reachables(p), 'sizeof(it)')
  6.  
    from java.util.properties p

toarray函数

此函数返回一个包含输入数组/枚举元素的数组。

unique函数

此函数返回包含给定输入数组/枚举的唯一元素的数组/枚举

示例:选择从字符串引用的唯一char []实例。请注意,多个string实例可以共享内容的相同char []。

  1.  
    // number of unique char[] instances referenced from any string
  2.  
    select count(unique(map(heap.objects('java.lang.string'), 'it.value')))
  3.  
     
  4.  
    // total number of strings
  5.  
    select count(heap.objects('java.lang.string'))

更复杂的例子

打印每个类加载器的直方图和由它加载的类的数量

select map(sort(map(heap.objects('java.lang.classloader'), 
   '{ loader: it, count: it.classes.elementcount }'), 'lhs.count < rhs.count'),
   'tohtml(it) + "<br>"')

上面的查询解释:java.lang.classloader有一个名为java.util.vector类型的的私有字段,vector有一个名为elementcount的私有字段,它是vector中元素的数量。我们使用javascript对象文字和地图功能选择多个值(加载器,计数)。我们使用带有比较表达式的sort函数对count(即加载的类数)进行排序。

 

查询每个类加载器实例的父子链

   select map(heap.objects('java.lang.classloader'),
      function (it) {
         var res = '';
         while (it != null) {
            res += tohtml(it) + "->";
            it = it.parent;
         }
         res += "null";
         return res + "<br>";
      })

请注意,我们使用java.lang.classloader类的字段并使用回调函数遍历parent为null以映射调用。

 

查询所有系统属性的值

  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;
            });

以上查询使用以下事实:

  • 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'字段。

 

请注意,此查询(以及许多其他查询)可能不稳定 - 因为java平台类的私有字段可能会被修改/删除而不会发出任何通知!(实施细节)。但是,在用户类上使用此类查询可能是安全的 - 假设用户可以控制类。

 

 

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网