2015-12-16 2 views
1

J'ai trouvé une tonne de questions sur « la taille retenue » et le accepted answer semble être:Calcul de la taille retenue, y compris les variables de cadre de pile?

La taille retenue pour un objet est la quantité de mémoire présente des objets conserves de collecte des ordures.

Maintenant, je travaille sur le calcul programmatique de la taille conservée dans un dossier hprof (tel que défini here), en utilisant le Netbeans profiler library (le calcul de la taille retenue est fait dans HprofHeap.java). Fonctionne très bien (désolé, utilisé Kotlin par souci de concision):

val heap: Heap = HeapFactory.createHeap(myHeap.toFile()) 
val threadClass: JavaClass = heap.getJavaClassByName("java.lang.Thread") 
val instanceFilter = { it: Instance -> threadClass == it.getJavaClass() } 
val sizeMap = heap.allInstances 
      .filter { instanceFilter(it) } 
      .toMap({ findThreadName(it) /* not shown */ }, { it.retainedSize }) 

Ce que je remarque quand le sizeMap avait seulement un nombre marginal de tailles retenues est que Netbeans calcule conserve la taille que pour les objets qui sont pas sur la pile. Donc, les variables locales (allouées sur la pile) affectées au Thread seraient incluses dans la taille retenue.

Ma question est la suivante: existe-t-il un moyen de faire en sorte que la bibliothèque netbeans considère les éléments de la pile comme des objets dépendants, par exemple le calculateur Yourkit Profiler? Comment pourrais-je ajouter une telle fonctionnalité si la réponse à la question précédente est "non"?

Répondre

1

Un peu de recherche a trouvé que le dumper de pile JVM crée une entrée de type ROOT JAVA FRAME pour une variable locale de pile (comparez VM_HeapDumper::do_thread). Depuis que je peux grep pour que dans le tas, voici ce que je faisais:

val threadClass: JavaClass = heap.getJavaClassByName("java.lang.Thread") 

val keyTransformer = { it: Instance -> findThreadName(it) } 
val instanceFilter = { it: Instance -> it.getJavaClass() == threadClass } 

val stackLocals = heap.gcRoots 
     .filter { it.kind == GCRoot.JAVA_FRAME } 
     .groupBy { (it as JavaFrameGCRoot).threadGCRoot } 

val sizeMap = heap.allInstances 
     .filter { instanceFilter(it) } 
     .toMap(
      { keyTransformer(it) }, 
      { 
       val locals = stackLocals[heap.getGCRoot(it)] 
       val localSize = locals!!.sumBy { it.instance.retainedSize.toInt() } 
       it.retainedSize + localSize 
      }) 

return Report(
     sizeMap.values.sum(), 
     sizeMap.keys.size.toLong(), 
     sizeMap.maxBy { it.value }?.let { it.toPair() } ?: ("n/a" to 0L)) 

Cette solution est basée sur la recherche de la racine de GC pour chaque thread (doit être lui-même Thread), puis trier à la racine gc stockée de le JAVA FRAME (l'ID de thread [= racine GC] fait partie des données d'entrée stockées).

Il ya encore une légère différence par rapport aux valeurs de Yourkit, probablement en raison de l'absence de ROOT JNI LOCAL entités, mais c'est assez proche pour moi.