坦白的說,有點標題黨,一股走進科學的感受,其實就是與你們分享一下關於OQL的一些內容。java
OQL是什麼?json
它是基於java堆上快照的對象查詢語言,語法呢,和咱們日常的SQL,HQL等等是近似的,畢竟QL一家親。數組
根據它堆上查詢的特性,因此天然是能夠進行內存診斷,或者出一個你想要的報表等等用途。bash
筆者本人也比較喜歡使用OQL,畢竟能本身寫代碼,仍是頗爲靈活,靈活天然是惹人親睞的因素。jvm
第二個緣由天然是: 工具
今天從介紹,到實際例子,帶給還不清楚「OQL」的盆友,多一種調試的思路。測試
正如《深刻理解java虛擬機》所說:「沒有什麼工具是'祕密武器',擁有了就能‘包治百病’」。 因此多一項技能,也只是多一種調試的思路。優化
若是你堅持看到了正文,相信你是瞭解heap dump
是何物,ui
因此內存結構等概念在本文不過多介紹。url
heap dump
分析工具衆多,從JConsole到Jhat,以及廣爲應用的MAT,它們自帶的功能已經很強大了,還用本身寫OQL嗎?
這個好理解,看Report和寫SQL,天然不是一個場景,前者是專一特定領域的結果,後者是靈活的開發語言。
先從簡單例子開始,後面筆者會用《Effective Java》中那段經典的「可能存在內存泄漏」示例代碼,展現怎樣寫出「有趣的OQL」。
第一步確定是搞一個heap dump
,既然是先舉簡單的例子,能夠隨便找一個heap dump
入手。
Tip: 每一個人的工具習慣不同,筆者今天的操做,好比heap dump,讀取hprof文件,以及執行OQL,都是在jVisualVm中進行的。
若是是有經驗的盆友,固然能夠跳過簡單的例子,繼續看下面的內容~
首先,先從左側列表中的幸運進程中抽取一位觀衆,進行heap dump,看名字應該是筆者正在跑的IDEA中內部進程。
不出意外,會跳到heapDump的分析頁面,其它功能暫且掠過,將左上角的下拉框選到今天的主題「OQL Console」。
查詢全部「字符串實例」,能夠算是OQL中的「hello world」了。 本文也不例外:
select
s
from
java.lang.String s
複製代碼
將以上OQL寫到下面的文本框中
輸入完OQL運行,上面就顯示了查詢結果,順着查詢結果,能夠找到該對象的值,引用等等信息。
這裏還有一個小細節,其實OQL也是有方言一說,只不過是受限於運行工具不一樣,
好比在有些書籍上是以select * from java.lang.String
做爲第一個入門語句,
可是在筆者當前的visualVM2.0中運行則會報錯,MAT下會正常輸出。
雖然大致語法是一致的,可是細節上,還要以你習慣使用工具的官網介紹的語法爲準。
若是你閱讀過Effective Java,那麼你必定記的有一段經典代碼,用於演示存在內存泄漏風險的場景。
若是沒有讀過也不要緊,這段代碼很簡單,筆者也粘貼了過來:
class Stack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack() {
elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
public void push(Object e) {
ensureCapacity();
elements[size++] = e;
}
public Object pop() {
if (size == 0) {
throw new EmptyStackException();
}
return elements[--size];
}
/**
* Ensure space for at least one more element, roughly
* doubling the capacity each time the array needs to grow.
*/
private void ensureCapacity() {
if (elements.length == size) {
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
}
複製代碼
代碼很簡單,一個本身實現的棧,出棧操做pop忘記了消除elements數組中的引用,存在內存泄漏的風險。
那麼筆者就用這段代碼作演示,看看OQL都能作些什麼。
筆者測試代碼:
public static void main(String[] args) throws Exception {
// stack1
Stack stack1 = new Stack();
// stack1入棧30個元素
addItem(stack1);
// stack2
Stack stack2 = new Stack();
// stack2入棧30個元素
addItem(stack2);
// stack2出棧20元素
for (int i = 0; i < 20; i++) {
stack2.pop();
}
System.out.println("----Over----");
// 通知full gc, 若是jvm心情不錯,能夠拿到dump
System.gc();
Thread.sleep(5000);
}
複製代碼
筆者的測試很簡單,實例化了兩個上文的棧,一個沒有出棧操做,一個有出棧操做,有出棧操做的實例,天然保存了過時的引用。
爲了拿到dump文件,我將啓動參數增長了下面兩條:
即FullGC以前dump一下。
繼續,一塊兒看看能夠用OQL作一些什麼事情? 下面的內容爲了省略篇幅,OQL和輸出結果,用文字Input和Output表示。
Input:
select
s.elements.length
from instanceof
com.vt.example.LeakTest$Stack s
複製代碼
Output:
33
33
複製代碼
能夠看出打印出來兩個Stack的實例結果,與測試內容一致。 而且其中elements的長度都是被擴容到了33。
由於結果輸出,支持json的格式,咱們不妨再多查詢一些內容。
Input:
select
{
"elements's length" :s.elements.length,
"instance": s,
"size attr" : s.size,
"count" : (actualCount = count(filter(s.elements, "it != null")))
}
from instanceof
com.vt.example.LeakTest$Stack s
複製代碼
Output:
{
instance = com.vt.example.LeakTest$Stack#1,
count = 30,
size attr = 30,
elements's length = 33 } { instance = com.vt.example.LeakTest$Stack#2, count = 30, size attr = 10, elements's length = 33
}
複製代碼
上面筆者的OQL語句分別查詢了,實例地址,實例size屬性,實例中數組容器的長度,以及真實的size大小。
真實的size大小是統計了數組容器中不爲null的元素。
也就是說,上文提到的stack問題是:出棧時沒有把過時的元算置於null,致使了內存泄漏風險。
那麼實際看看上面的OQL結果,是否是知道了什麼?
沒錯,那就是第二個實例實際保存的元素,和size屬性大小不符,天然是已經發生問題的實例。
可是看的還不是那麼清晰,咱們再優化一個版本看看如何:
Input:
select
{
"實例中數組容器當前大小" :s.elements.length,
"實例地址": s,
"當前size屬性" : s.size,
"實際保存的引用數量" : (actualCount = count(filter(s.elements, "it != null"))),
"疑似泄漏" : (actualCount > s.size) ? "<font color='red'>有</font>" : "無"
}
from instanceof
com.vt.example.LeakTest$Stack s
複製代碼
Output:
最後這版本用了中文描述,而且還有文字高亮,是否是更清晰了一些?
用OQL直接查出咱們想要的一個報表,依靠基本功能可不必定能作獲得哦。
看到這,你們應該對OQL有一個初步的瞭解和如何簡單「玩轉」了,本文也就結束了。
實際中,可不是區區上面的OQL就能查出來內存泄漏,內容是演示功能爲主,帶給不瞭解OQL的讀者一套新的思路,可不是什麼解決方案啊~