2011-11-23 10 views
12

Sto facendo un'analisi della memoria di un software java esistente. Esiste un sql 'group di' equivalente in oql per vedere il conteggio di oggetti con gli stessi valori ma diverse istanze.analisi heap java con oql: conteggio stringhe univoche

select count (*) da java.lang.String s gruppo da s.toString()

mi piacerebbe realizzare una lista di stringhe duplicate insieme al numero di duplicati. Lo scopo di questo è vedere i casi con numeri grandi in modo che possano essere ottimizzati usando String.intern().

Esempio:

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

ecc ...

risposta

19

Di seguito è basata sulla risposta da Peter Dolberg e può essere utilizzato nella 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; 
    } 
    } 
); 

Si inizia utilizzando una chiamata map() su tutte le istanze String e per ogni stringa creazione o l'aggiornamento di un oggetto nell'array counts. Ogni oggetto ha un campo string e un count.

L'array risultante conterrà una voce per ogni istanza String, ciascuna con un valore count più grande della voce precedente per la stessa stringa. Il risultato viene quindi ordinato sul campo count e il risultato simile a questa:

{ 
count = 1028.0, 
string = *null* 
} 

{ 
count = 1027.0, 
string = *null* 
} 

{ 
count = 1026.0, 
string = *null* 
} 

... 

(nel mio test della stringa "*null*" è stato il più comune).

L'ultimo passaggio consiste nel filtrare ciò utilizzando una funzione che restituisce true per la prima occorrenza di ciascuna stringa. Usa l'array alreadyReturned per tenere traccia di quali stringhe sono già state incluse.

+1

Grazie che risolve bene il problema. L'oql è in qualche modo scomodo da usare. Tutto deve succedere in una funzione ... – paweloque

+0

wow, non sapevo che jvisualvm è così potente. Ho trovato valori di conteggio elevato per alcune stringhe: il tuo codice esclude la garbage (non le stringhe di riferimento)? – Jan

+1

Usa "heap.objects" per trovare tutti gli oggetti java.lang.String nell'heap. Non esiste alcun filtro per escludere le stringhe senza riferimento. Ma a seconda di come è stato generato il dump dell'heap, la JVM potrebbe aver già eseguito un GC completo, nel qual caso qualsiasi stringa senza riferimento avrebbe dovuto essere già stata rimossa e non inclusa nel dump dell'heap. –

2

Purtroppo, non c'è un equivalente a "gruppo da" in OQL. Suppongo che tu stia parlando dell'OQL usato in jhat e VisualVM.

C'è un'alternativa, però. Se usi la pura sintassi JavaScript invece della sintassi "seleziona x da y", allora hai tutta la potenza di JavaScript con cui lavorare.

Anche così, il modo alternativo di ottenere le informazioni che stai cercando non è semplice. Per esempio, ecco un OQL "query" che eseguirà lo stesso compito come query:

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

In questo esempio un normale oggetto imita JavaScript un insieme (raccolta senza duplicazioni). Mentre la funzione map attraversa ogni stringa, il set viene usato per determinare se la stringa è già stata vista. I duplicati non contano per il totale (return 0) ma le nuove stringhe fanno (return 1).

+0

Ciao Peter, grazie per la tua ricerca, mi porta nella direzione, ma non sono ancora lì :) Con questa query vedo il numero totale di duplicati stringhe. Quello che mi piacerebbe vedere è la stringa e il numero di ripetizione: 'foo' 10 volte, 'bar' 100 volte, ecc. Per vedere che ho provato a produrre il contenuto del set, ma ottengo solo strane eccezioni jscript .. Hai un'idea di come ottenere ciò che voglio vedere? – paweloque

7

Vorrei utilizzare Eclipse Memory Analyzer.

+2

Mi piace molto la tua proposta perché risolve il problema molto bene. Spero, tuttavia, che capirai che la taglia va a Johan Kaving per aver scritto l'oql. Penso che ci possano essere situazioni in cui è utile capire oql. Ma grazie comunque! – paweloque

+0

Per fare ciò usate Open Query Browser -> Java Basics -> Group By Value. Per gli oggetti selezionare 'java.lang.String' e per selezionare' value'. – kichik

0

Pubblica semplicemente la mia soluzione ed esperienza quando faccio un problema simile per altri riferimenti.

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

Il precedente codice in uscita volontà primi 10 classi utilizzate da java.lang.ref.Finalizer.
Suggerimenti:
1. La funzione di ordinamento utilizzando la funzione XXX NON funziona sul mio Mac OS.
2. La funzione classof può restituire la classe del referente. (Ho provato a utilizzare fobject.referent.toString() -> questo ha restituito un sacco di org.netbeans.lib.profiler.heap.InstanceDump, anche questo ha sprecato molto del mio tempo).

1

Una query molto più efficiente:

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