2011-11-23 10 views
12

Robię analizę pamięci istniejącego oprogramowania java. Czy istnieje odpowiednik sql 'group by' w oql, aby zobaczyć liczbę obiektów o tych samych wartościach, ale różnych instancjach.Analiza sterty java z oql: Policz unikalne ciągi znaków

select count (*) z java.lang.String s grupy przez s.toString()

Chciałbym uzyskać listę duplikatów ciągów wraz z liczbą powtórzeń. Celem tego jest zobaczenie przypadków z dużymi liczbami, aby można było je zoptymalizować za pomocą String.intern().

przykład:

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

itd ...

Odpowiedz

19

bazuje na odpowiedź Petera Dolberg i może być używany w VisualVM OQL konsoli następujące:

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

Zaczyna stosując map() wezwanie na wszystkich przypadkach Smyczkowych i dla każdej struny tworzenia lub aktualizacji obiekt w tablicy counts. Każdy obiekt ma pole string i count.

Wynikowa tablica będzie zawierała jeden wpis dla każdej instancji String, z których każda ma wartość o jeden większą niż poprzednia pozycja dla tego samego ciągu znaków. Wynik jest następnie sortowane na polu count i wynik wygląda mniej więcej tak:

{ 
count = 1028.0, 
string = *null* 
} 

{ 
count = 1027.0, 
string = *null* 
} 

{ 
count = 1026.0, 
string = *null* 
} 

... 

(w moim teście String "*null*" był najczęściej).

Ostatnim krokiem jest filtrowanie tego przy użyciu funkcji, która zwraca wartość true dla pierwszego wystąpienia każdego ciągu. Korzysta z tablicy alreadyReturned, aby śledzić, które łańcuchy zostały już dołączone.

+1

Dzięki, że ładnie rozwiązuje problem. Oql jest jakoś niewygodny w użyciu. Wszystko musi się zdarzyć w jednej funkcji ... – paweloque

+0

wow, nie wiedziałem, że jvisualvm jest tak potężny. Znalazłem wysokie wartości liczników dla niektórych Ciągów - czy twój kod wyklucza śmieci (nie odnoszące się do Ciągów)? – Jan

+1

Używa "heap.objects" do znalezienia wszystkich obiektów java.lang.String na stercie. Nie ma filtrowania, aby wykluczyć niezwiązane łańcuchy. Ale w zależności od sposobu generowania zrzutu sterty JVM mogło wykonać pełny GC przed, w którym to przypadku wszelkie niezwiązane łańcuchy powinny już zostać usunięte i nie zostały uwzględnione w zrzucie sterty. –

2

Niestety, nie jest to równoznaczne z "grupa się" w OQL. Zakładam, że mówisz o OQL używanym w jhat i VisualVM.

Istnieje jednak alternatywa. Jeśli używasz czystej składni JavaScript zamiast składni "wybierz x z Y", masz pełną moc JavaScriptu do pracy.

Mimo to alternatywny sposób uzyskania potrzebnych informacji nie jest prosty. Na przykład, oto OQL „zapytanie”, która wykona to samo zadanie jak zapytaniu:

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

W tym przykładzie regularne JavaScript Object naśladuje Komplet (kolekcja bez duplikatów). Ponieważ funkcja mapy przechodzi przez każdy ciąg znaków, zestaw służy do określenia, czy ciąg znaków został już zauważony. Duplikaty nie liczą się do sumy (return 0), ale nowe ciągi robią (return 1).

+0

Cześć Piotrze, dzięki za zapytaniu, to prowadzi mnie w kierunku, ale nie jestem jeszcze tam :) Z tego zapytania widzę całkowitą liczbę dwóch egzemplarzach smyczki. Chciałbym zobaczyć łańcuch i liczbę powtórzeń: "foo" 10 razy, "pasek" 100 razy, itd. Aby zobaczyć, że próbowałem wyprowadzić zawartość zestawu, ale dostaję tylko dziwne wyjątki jscript .. Czy masz pomysł, jak osiągnąć to, co chcę zobaczyć? – paweloque

7

Zamiast tego użyłbym Eclipse Memory Analyzer.

+2

Bardzo podoba mi się twoja propozycja, ponieważ bardzo ładnie rozwiązuje problem. Mam jednak nadzieję, że zrozumiesz, że nagroda trafia do Johana Kavinga za napisanie oql. Myślę, że mogą zaistnieć sytuacje, w których warto zrozumieć oql. Ale niz jednak! – paweloque

+0

Aby to zrobić, użyj przeglądarki zapytań do otwartych zapytań -> Podstawy Java -> Grupuj według wartości. Dla obiektów wybierz 'java.lang.String', a dla pola wybierz" wartość ". – kichik

0

Wystarczy, że opublikujesz moje rozwiązanie i doświadczysz podobnych problemów w przypadku innych odniesień.

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

Poprzedni kod wyświetli 10 najlepszych klas używanych przez java.lang.ref.Finalizer.
Wskazówki:
1. Funkcja sortowania za pomocą funkcji XXX NIE działa na moim Mac OS.
2. Funkcja classof może zwrócić klasę odniesienia. (Próbowałem użyć fobject.referent.toString() -> to zwróciło wiele org.netbeans.lib.profiler.heap.InstanceDump.To także zmarnowało dużo mojego czasu).

1

O wiele bardziej efektywne zapytania:

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