2011-11-23 13 views
12

Estoy haciendo un análisis de memoria de un software de java existente. ¿Hay un sql 'group by' equivalente en oql para ver el recuento de objetos con los mismos valores pero diferentes instancias?análisis de heap de java con oql: Count unique strings

select count (*) de java.lang.String s grupo por s.toString()

me gustaría conseguir una lista de cadenas duplicadas junto con el número de duplicados. El propósito de esto es ver los casos con números grandes para que puedan optimizarse usando String.intern().

Ejemplo:

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

etc ...

Respuesta

19

El siguiente se basa en la respuesta de Peter Dolberg y se puede utilizar en el VisualVM NCO Consola:

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

Se inicia mediante una llamada map() sobre todas las instancias de String y para cada cadena de crear o actualizar una objeto en la matriz counts. Cada objeto tiene un campo string y count.

La matriz resultante contendrá una entrada para cada instancia de Cadena, cada una con un valor count mayor que la entrada anterior para la misma Cadena. El resultado se ordena a continuación, en el campo count y el resultado se ve algo como esto:

{ 
count = 1028.0, 
string = *null* 
} 

{ 
count = 1027.0, 
string = *null* 
} 

{ 
count = 1026.0, 
string = *null* 
} 

... 

(en mi prueba de la Cadena "*null*" fue el más común).

El último paso es filtrar esto utilizando una función que devuelve verdadero para la primera aparición de cada cadena. Utiliza la matriz alreadyReturned para realizar un seguimiento de las cadenas que ya se han incluido.

+1

Gracias que resuelve muy bien el problema. El oql es de alguna manera incómodo de usar. Todo tiene que suceder en una función ... – paweloque

+0

wow, no sabía que jvisualvm es tan poderoso. Encontré valores de recuento altos para algunas cadenas. ¿Su código excluye la basura (cadenas no referenciadas)? – Jan

+1

Utiliza "heap.objects" para buscar todos los objetos java.lang.String en el montón. No hay filtrado para excluir cadenas no referenciadas. Pero dependiendo de cómo se generó el volcado del montón, la JVM puede haber realizado un GC completo antes, en cuyo caso cualquier cadena no referenciada ya debería haber sido eliminada y no incluida en el volcado del heap. –

2

Lamentablemente, no existe un equivalente al "grupo por" en NCO. Supongo que estás hablando del OQL que se usa en jhat y VisualVM.

Sin embargo, existe una alternativa. Si usa la sintaxis pura de JavaScript en lugar de la sintaxis "select x from y", entonces tiene todo el poder de JavaScript para trabajar.

Aún así, la forma alternativa de obtener la información que está buscando no es simple. Por ejemplo, aquí está una "consulta" NCO que llevará a cabo la misma tarea que su consulta:

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

En este ejemplo, un objeto JavaScript regulares imita un Conjunto (recopilación, sin duplicados). Como la función del mapa pasa por cada cadena, el conjunto se usa para determinar si la cadena ya se ha visto. Los duplicados no cuentan para el total (return 0) pero sí lo hacen las cadenas nuevas (return 1).

+0

Hola Pedro, gracias por su consulta, me pone en la dirección, pero no estoy todavía allí :) Con esta consulta veo el número total de duplicado instrumentos de cuerda. Lo que me gustaría ver es la cadena y el número de repetición: 'foo' 10 veces, 'bar' 100 veces, etc. Para ver que traté de generar el contenido del conjunto, pero solo recibo extrañas excepciones de jscript .. ¿Tienes una idea de cómo lograr lo que quiero ver? – paweloque

7

Yo usaría Eclipse Memory Analyzer en su lugar.

+2

Me gusta mucho su propuesta porque resuelve el problema muy bien. Espero, sin embargo, que comprenda que la recompensa va para Johan Kaving por escribir el pedido. Creo que puede haber situaciones en las que sea útil comprender oql. ¡Pero gracias, sin embargo! – paweloque

+0

Para hacer eso, use el Explorador de consultas abiertas -> Conceptos básicos de Java -> Agrupar por valor. Para los objetos, seleccione 'java.lang.String' y para el campo seleccione' value'. – kichik

0

Solo publique mi solución y experiencia cuando realice un problema similar para otras referencias.

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

El código anterior mostrará las 10 clases principales utilizadas por java.lang.ref.Finalizer.
Sugerencias:
1. La función de clasificación mediante la función XXX NO funciona en mi Mac OS.
2. La clase de función puede devolver la clase del referente. (Intenté usar fobject.referent.toString() -> esto me devolvió una gran cantidad de org.netbeans.lib.profiler.heap.InstanceDump. Esto también desperdició mucho de mi tiempo).

1

Una consulta mucho más eficiente:

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