天天看點

JVM 對象查詢語言(OQL)

本文主要翻譯自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 int[] a where a.length >= 256

  • 顯示與正規表達式比對的字元串的内容
    select s.value.toString() from java.lang.String s 
    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可以找到它。
JVM 對象查詢語言(OQL)

OQL内置對象,函數

堆對象

該堆内置對象支援下列方法:

  • heap.forEachClass - 為每個Java類調用一個回調函數
  • 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被賦予的對象
  • 選擇所有比對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中的所有可通路内容,但省略可通過指定字段通路的對象。
select reachables(u, 'java.net.URL.handler') from java.net.URL u
           

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對象

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對象文字或數組選擇多個值。

示例:顯示每個線程對象的名稱和線程

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

數組/疊代器/枚舉操作函數

這些函數接受數組/疊代器/枚舉和表達式字元串[或回調函數]作為輸入。這些函數疊代數組/疊代器/枚舉,并在每個元素上應用表達式(或函數)。請注意,JavaScript對象是關聯數組。是以,這些函數也可以與任意JavaScript對象一起使用。

concat函數

連接配接兩個數組或枚舉(即傳回複合枚舉)。

contains函數

傳回給定的數組/枚舉是否包含代碼中指定的給定布爾表達式的元素。評估的代碼可以引用以下内置變量。

  • it - >目前通路過的元素
  • index - >目前元素的索引
  • array - >正在疊代的數組/枚舉

示例:選擇某些靜态字段引用某些類的所有Properties對象。

select p from java.util.Properties p
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對象的所有引用
    select filter(referrers(u), "! /java.net./.test(classof(it).name)")
    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'))
               
  • 查找具有最大長度的字元串執行個體
    select max(heap.objects('java.lang.String'), 'lhs.value.length > rhs.value.length')
               

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對象中可到達對象的大小總和

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函數

此函數傳回一個包含輸入數組/枚舉元素的數組。

unique函數

此函數傳回包含給定輸入數組/枚舉的唯一進制素的數組/枚舉

示例:選擇從字元串引用的唯一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 }'), '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平台類的私有字段可能會被修改/删除而不會發出任何通知!(實施細節)。但是,在使用者類上使用此類查詢可能是安全的 - 假設使用者可以控制類。