2011-11-23 9 views
12

Im đang thực hiện phân tích bộ nhớ của phần mềm java hiện có. Có một nhóm sql 'bằng' tương đương trong oql để xem số lượng đối tượng có cùng giá trị nhưng các trường hợp khác nhau không.phân tích heap java với oql: Đếm chuỗi duy nhất

select count (*) từ nhóm java.lang.String s bởi s.toString()

Tôi muốn đạt được một danh sách các chuỗi trùng lặp cùng với số lượng bản sao. Mục đích của việc này là để xem các trường hợp có số lượng lớn để chúng có thể được tối ưu hóa bằng cách sử dụng String.intern().

Ví dụ:

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

vv ...

Trả lời

19

Sau đây là dựa trên câu trả lời của Peter Dolberg và có thể được sử dụng trong VisualVM OQL Console:

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

Nó bắt đầu bằng cách sử dụng một cuộc gọi map() trên tất cả các trường String và cho mỗi chuỗi tạo hoặc cập nhật đối tượng trong mảng counts. Mỗi đối tượng có một trường string và trường count.

Mảng kết quả sẽ chứa một mục nhập cho mỗi thể hiện String, mỗi giá trị có giá trị count lớn hơn mục nhập trước đó cho cùng một Chuỗi. Kết quả là sau đó được sắp xếp trên các lĩnh vực count và kết quả trông giống như sau:

{ 
count = 1028.0, 
string = *null* 
} 

{ 
count = 1027.0, 
string = *null* 
} 

{ 
count = 1026.0, 
string = *null* 
} 

... 

(trong thử nghiệm của tôi String "*null*" là phổ biến nhất).

Bước cuối cùng là lọc bộ lọc này bằng hàm trả về true cho lần xuất hiện đầu tiên của mỗi chuỗi. Nó sử dụng mảng alreadyReturned để theo dõi chuỗi nào đã được bao gồm.

+1

Cảm ơn bạn đã giải quyết được vấn đề. Các oql là bằng cách nào đó khó xử để sử dụng. Tất cả phải xảy ra trong một chức năng ... – paweloque

+0

wow, không biết rằng jvisualvm là mạnh mẽ. Tôi tìm thấy giá trị đếm cao cho một số Strings - không mã của bạn loại trừ rác thải (không tham chiếu Strings)? – Jan

+1

Nó sử dụng "heap.objects" để tìm tất cả các đối tượng java.lang.String trên heap. Không có bộ lọc để loại trừ các chuỗi không tham chiếu. Nhưng tùy thuộc vào cách đống kết xuất được tạo ra, JVM có thể đã thực hiện một GC đầy đủ trước đó, trong trường hợp này, bất kỳ chuỗi không tham chiếu nào đã bị loại bỏ và không được bao gồm trong vùng chứa đống. –

2

Đáng buồn thay, có không phải là một tương đương với "nhóm bằng cách" trong OQL. Tôi giả sử bạn đang nói về OQL được sử dụng trong jhat và VisualVM.

Có một giải pháp thay thế. Nếu bạn sử dụng cú pháp JavaScript thuần túy thay vì cú pháp "select x from y" thì bạn có toàn bộ sức mạnh của JavaScript để làm việc.

Mặc dù vậy, cách thay thế để nhận thông tin bạn đang tìm kiếm không hề đơn giản. Ví dụ: đây là truy vấn "OQL" sẽ thực hiện nhiệm vụ tương tự như truy vấn của bạn:

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

Trong ví dụ này, đối tượng JavaScript thông thường bắt chước một tập hợp (bộ sưu tập không trùng lặp). Khi chức năng bản đồ đi qua từng chuỗi, tập hợp được sử dụng để xác định xem chuỗi đã được xem chưa. Các bản sao không được tính vào tổng số (trả về 0) nhưng các chuỗi mới làm (trả về 1).

+0

Hi Peter, cảm ơn cho câu hỏi của bạn, nó mang lại cho tôi vào hướng, nhưng tôi chưa có :) Với truy vấn này tôi thấy tổng số trùng lặp dây. Những gì tôi muốn xem là chuỗi và lặp lại số: 'foo' 10 lần, 'bar' 100 lần, vv .. Để thấy rằng tôi đã cố gắng để xuất nội dung của bộ, nhưng tôi chỉ nhận được ngoại lệ jscript lạ .. Bạn có một ý tưởng làm thế nào để đạt được những gì tôi muốn xem? – paweloque

7

Tôi sẽ sử dụng Eclipse Memory Analyzer để thay thế.

+2

Tôi thực sự thích đề xuất của bạn bởi vì nó giải quyết vấn đề rất độc đáo. Tôi hy vọng, tuy nhiên, bạn sẽ hiểu rằng tiền thưởng đi đến Johan Kaving để viết oql. Tôi nghĩ rằng có thể có những tình huống mà nó rất hữu ích để hiểu oql. Nhưng cám ơn tuy nhiên! – paweloque

+0

Để thực hiện điều đó, hãy sử dụng Trình duyệt truy vấn mở -> Khái niệm cơ bản về Java -> Nhóm theo giá trị. Đối với các đối tượng, chọn 'java.lang.String' và cho trường chọn' giá trị'. – kichik

0

Chỉ cần đăng giải pháp và kinh nghiệm của tôi khi thực hiện vấn đề tương tự cho các tham chiếu khác.

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

Mã trước sẽ xuất ra 10 lớp hàng đầu được java.lang.ref.Finalizer sử dụng.
Mẹo:
1. Chức năng sắp xếp bằng cách sử dụng hàm XXX KHÔNG hoạt động trên Mac OS của tôi.
2. Hàm classof có thể trả về lớp của tham chiếu. (Tôi đã cố gắng sử dụng fobject.referent.toString() -> điều này trả về rất nhiều org.netbeans.lib.profiler.heap.InstanceDump. Điều này cũng lãng phí rất nhiều thời gian của tôi).

1

Một truy vấn hiệu quả hơn:

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