2011-11-23 50 views
12

我正在做一個現有java軟件的內存分析。在oql中是否有一個sql'group by'等同的值來查看具有相同值但不同實例的對象數。用oql進行java堆分析:計算唯一字符串

SELECT COUNT(*) 從java.lang.String中小號 組由s.toString()

我想與重複次數一起實現重複的字符串列表。這樣做的目的是查看大數字的情況,以便使用String.intern()進行優化。

實施例:

"foo" 100 
"bar" 99 
"lazy fox" 50 

等...

回答

19

是基於彼得Dolberg的答案,可以在VisualVM OQL控制檯中使用下列內容:

var counts={}; 
var alreadyReturned={}; 

filter(
    sort(
    map(heap.objects("java.lang.String"), 
    function(heapString){ 
     if(! counts[heapString.toString()]){ 
     counts[heapString.toString()] = 1; 
     } else { 
     counts[heapString.toString()] = counts[heapString.toString()] + 1; 
     } 
     return { string:heapString.toString(), count:counts[heapString.toString()]}; 
    }), 
    'lhs.count < rhs.count'), 
    function(countObject) { 
    if(! alreadyReturned[countObject.string]){ 
     alreadyReturned[countObject.string] = true; 
     return true; 
    } else { 
     return false; 
    } 
    } 
); 

它首先使用map()調用了所有的字符串實例,併爲每個字符串創建或更新對象在counts數組中。每個對象都有一個string和一個count字段。

結果數組將爲每個String實例包含一個條目,每個條目的值爲比相同String的前一條目大1的count值。 然後將結果排序的count場,結果看起來是這樣的:

{ 
count = 1028.0, 
string = *null* 
} 

{ 
count = 1027.0, 
string = *null* 
} 

{ 
count = 1026.0, 
string = *null* 
} 

... 

(在我的測試字符串"*null*"是最常見的)。

最後一步是使用函數對其進行過濾,該函數在每個String的第一次出現時返回true。它使用​​數組來跟蹤哪些字符串已包含在內。

+1

謝謝,很好地解決了這個問題。 oql在某種程度上使用起來很尷尬。這一切都必須發生在一個功能... – paweloque

+0

哇,不知道jvisualvm是如此強大。我發現某些字符串的計數值很高 - 您的代碼是否排除垃圾(未引用的字符串)? – Jan

+1

它使用「heap.objects」來查找堆中的所有java.lang.String對象。沒有過濾來排除未引用的字符串。但是,根據堆轉儲的生成方式,JVM可能在之前執行了完整的GC,在這種情況下,任何未引用的字符串應該已經被刪除並且不包含在堆轉儲中。 –

2

不幸的是,沒有一個等效 「基團通過」 在OQL到。我假設你正在討論在jhat和VisualVM中使用的OQL。

雖然有一個選擇。如果您使用純JavaScript語法而不是「從x選擇x」語法,那麼您就可以使用JavaScript的全部功能。

即便如此,獲取您要查找的信息的另一種方法並不簡單。例如,這裏是一個OQL「查詢」,將執行相同的任務,您的查詢:

var set={}; 
sum(map(heap.objects("java.lang.String"),function(heapString){ 
    if(set[heapString.toString()]){ 
    return 0; 
    } 
    else{ 
    set[heapString.toString()]=true; 
    return 1; 
    } 
})); 

在這個例子中常規的JavaScript對象模仿一組(集合,沒有重複)。當映射函數遍歷每個字符串時,該集合用於確定字符串是否已經被看到。重複不計入總數(返回0),但新字符串(返回1)。

+0

彼得你好,感謝您的查詢,它帶給我進了方向,但我尚未有:)有了這個查詢我看到重複的總數字符串。我想看到的是string和repeat-number:'foo'10次,'bar'100次等等。爲了看到我試圖輸出集合的內容,但是我只能得到奇怪的jscript異常..你有一個想法如何實現我想看到的? – paweloque

7

我會用Eclipse Memory Analyzer來代替。

+2

我非常喜歡你的建議,因爲它很好地解決了這個問題。不過,我希望你會明白,賞金要寫給Johan Kaving編寫oql。我想可能有些情況下,瞭解oql是有用的。但是,謝謝! – paweloque

+0

爲此,請使用打開查詢瀏覽器 - > Java基礎 - >按值分組。對於對象選擇'java.lang.String'和字段選擇'value'。 – kichik

0

只要發佈我的解決方案和經驗時,爲其他參考做類似的問題。

var counts = {}; 
var alreadyReturned = {}; 
top(
filter(
    sort(
     map(heap.objects("java.lang.ref.Finalizer"), 
      function (fobject) { 
       var className = classof(fobject.referent) 
       if (!counts[className]) { 
        counts[className] = 1; 
       } else { 
        counts[className] = counts[className] + 1; 
       } 
       return {string: className, count: counts[className]}; 
      }), 
     'rhs.count-lhs.count'), 
    function (countObject) { 
     if (!alreadyReturned[countObject.string]) { 
      alreadyReturned[countObject.string] = true; 
      return true; 
     } else { 
      return false; 
     } 
    }), 
    "rhs.count > lhs.count", 10); 

上面的代碼將輸出java.lang.ref.Finalizer使用的前10個類。
溫馨提示:
1.使用功能XXX的排序功能不適用於我的Mac OS。
2.該類函數可以返回指示對象的類。 (我試圖使用fobject.referent.toString() - >這返回了很多org.netbeans.lib.profiler.heap.InstanceDump。這也浪費了我很多時間)。

1

一個更爲高效的查詢:

var countByValue = {}; 

// Scroll the strings 
heap.forEachObject(
    function(strObject) { 
    var key = strObject.toString(); 
    var count = countByValue[key]; 
    countByValue[key] = count ? count + 1 : 1; 
    }, 
    "java.lang.String", 
    false 
); 

// Transform the map into array 
var mapEntries = []; 
for (var i = 0, keys = Object.keys(countByValue), total = keys.length; i < total; i++) { 
    mapEntries.push({ 
    count : countByValue[keys[i]], 
    string : keys[i] 
    }); 
} 

// Sort the counts 
sort(mapEntries, 'rhs.count - lhs.count'); 
相關問題