1. 程式人生 > 實用技巧 >JVM 物件查詢語言(OQL)jmap生成的dump檔案分析用到

JVM 物件查詢語言(OQL)jmap生成的dump檔案分析用到

轉自:https://blog.csdn.net/pange1991/article/details/82023771做了一些修改

本文主要翻譯自JDK 1.8的JVM監控工具jhat中關於OQL的英文幫助說明。

可以在jhat 和 jvisualvm 中進行實踐。

OQL(物件查詢語言)

OQL是用於查詢Java堆的類SQL查詢語言。OQL允許過濾/選擇從Java堆中獲取的資訊。雖然HAT已經支援預定義的查詢,例如“顯示類X的所有例項”,但OQL增加了更多的靈活性。OQL基於JavaScript表示式語言。

OQL查詢的形式

  1. select <JavaScript expression to select>
  2. [ from [instanceof] <class name> <identifier>
  3. [ 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

  • 顯示與正則表示式匹配的字串的內容【注意select s不要寫成select s.value.toString()否則沒有String的索引資訊了,這裡後面的s表示從型別String裡獲取值包裝成s,s.value則是String型別值,因此最後的s.value.toString()其實可以直接s.value即可
    1. select s from java.lang.String s
    2. where /pattern/.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);
    
    
    whereclassName是要查詢的類的名稱。生成的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中的所有可訪問內容,但省略可通過指定欄位訪問的物件。
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物件
    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'))
    
    
  • 查詢具有最大長度的字串例項
    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物件中可到達物件的大小總和

  1. select sum(map(reachables(p), 'sizeof(it)'))
  2. from java.util.Properties p
  3. // or omit the map as in ...
  4. select sum(reachables(p), 'sizeof(it)')
  5. 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. // total number of Strings
  4. select count(heap.objects('java.lang.String'))

更復雜的例子

列印每個類載入器的直方圖和由它載入的類的數量

  1. select map(sort(map(heap.objects('java.lang.ClassLoader'),
  2. '{ loader: it, count: it.classes.elementCount }'), 'lhs.count < rhs.count'),
  3. 'toHtml(it) + "<br>"')

上面的查詢解釋:java.lang.ClassLoader有一個名為java.util.Vector型別的的私有欄位,Vector有一個名為elementCount的私有欄位,它是Vector中元素的數量。我們使用JavaScript物件文字和地圖功能選擇多個值(載入器,計數)。我們使用帶有比較表示式的sort函式對count(即載入的類數)進行排序。

查詢每個類載入器例項的父子鏈

  1. select map(heap.objects('java.lang.ClassLoader'),
  2. function (it) {
  3. var res = '';
  4. while (it != null) {
  5. res += toHtml(it) + "->";
  6. it = it.parent;
  7. }
  8. res += "null";
  9. return res + "<br>";
  10. })

請注意,我們使用java.lang.ClassLoader類的欄位並使用回撥函式遍歷parent為null以對映呼叫。

查詢所有系統屬性的值

  1. select map(filter(heap.findClass('java.lang.System').statics.props.table, 'it != null'),
  2. function (it) {
  3. var res = "";
  4. while (it != null) {
  5. res += it.key.value.toString() + '=' +
  6. it.value.value.toString() + '<br>';
  7. it = it.next;
  8. }
  9. return res;
  10. });

以上查詢使用以下事實:

  • 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平臺類的私有欄位可能會被修改/刪除而不會發出任何通知!(實施細節)。但是,在使用者類上使用此類查詢可能是安全的 - 假設使用者可以控制類