Question En Java, quelle est la meilleure façon de déterminer la taille d'un objet?


Par exemple, disons que j'ai une application qui peut lire dans un fichier CSV avec des piles de lignes de données. Je donne à l'utilisateur un résumé du nombre de lignes en fonction des types de données, mais je veux m'assurer de ne pas lire trop de lignes de données et de provoquer OutOfMemoryErrors. Chaque ligne se traduit par un objet. Y at-il un moyen facile de trouver la taille de cet objet par programmation? Existe-t-il une référence qui définit la taille des types primitifs et des références d'objet pour un VM?

En ce moment, j'ai un code qui dit lire jusqu'à 32 000 lignes, mais je voudrais aussi avoir un code qui dit lire autant de lignes que possible jusqu'à ce que j'ai utilisé 32 Mo de la mémoire. Peut-être que c'est une question différente, mais j'aimerais quand même savoir.


533
2017-09-09 17:07


origine


Réponses:


Vous pouvez utiliser le package java.lang.instrument

Compilez et mettez cette classe dans un fichier JAR:

import java.lang.instrument.Instrumentation;

public class ObjectSizeFetcher {
    private static Instrumentation instrumentation;

    public static void premain(String args, Instrumentation inst) {
        instrumentation = inst;
    }

    public static long getObjectSize(Object o) {
        return instrumentation.getObjectSize(o);
    }
}

Ajoutez ce qui suit à votre MANIFEST.MF:

Premain-Class: ObjectSizeFetcher

Utilisez getObjectSize:

public class C {
    private int x;
    private int y;

    public static void main(String [] args) {
        System.out.println(ObjectSizeFetcher.getObjectSize(new C()));
    }
}

Invoquer avec:

java -javaagent:ObjectSizeFetcherAgent.jar C

403
2017-09-09 19:24



Il y a quelques années Javaworld avait un article sur la détermination de la taille des objets Java composites et potentiellement imbriqués, ils marchent essentiellement à travers la création d'une implémentation de sizeof () en Java. L'approche s'appuie essentiellement sur d'autres travaux où les gens ont identifié expérimentalement la taille des primitives et des objets Java typiques, puis appliquent ces connaissances à une méthode qui parcourt de manière récursive un graphe d'objet afin d'obtenir la taille totale.

Il sera toujours un peu moins précis qu'une mise en œuvre en C native simplement à cause des choses qui se passent dans les coulisses d'une classe, mais cela devrait être un bon indicateur.

Sinon, un projet SourceForge appelé de manière appropriée taille de qui offre une bibliothèque Java5 avec une implémentation sizeof ().

P.S. N'utilisez pas l'approche de sérialisation, il n'y a pas de corrélation entre la taille d'un objet sérialisé et la quantité de mémoire qu'il consomme lorsqu'il est en ligne.


69
2017-09-09 18:42



Tu devrais utiliser jol, un outil développé dans le cadre du projet OpenJDK.

JOL (Java Object Layout) est la petite boîte à outils pour analyser les schémas de disposition d'objets dans les JVM. Ces outils utilisent fortement Unsafe, JVMTI et Serviceability Agent (SA) pour décoder la mise en page, l'empreinte et les références de l'objet. Cela rend JOL beaucoup plus précis que d'autres outils reposant sur des vidages de tas, des hypothèses de spécification, etc.

Pour obtenir la taille des primitives, des références et des éléments de tableau, utilisez VMSupport.vmDetails(). Sur Oracle JDK 1.8.0_40 fonctionnant sous Windows 64 bits (utilisé pour tous les exemples suivants), cette méthode renvoie

Running 64-bit HotSpot VM.
Using compressed oop with 0-bit shift.
Using compressed klass with 3-bit shift.
Objects are 8 bytes aligned.
Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

Vous pouvez obtenir la taille peu profonde d'une instance d'objet en utilisant ClassLayout.parseClass(Foo.class).toPrintable() (en passant une instance à toPrintable). C'est seulement l'espace consommé par une seule instance de cette classe; il n'inclut aucun autre objet référencé par cette classe. Il Est-ce que inclure le surdébit VM pour l'en-tête de l'objet, l'alignement du champ et le remplissage. Pour java.util.regex.Pattern:

java.util.regex.Pattern object internals:
 OFFSET  SIZE        TYPE DESCRIPTION                    VALUE
      0     4             (object header)                01 00 00 00 (0000 0001 0000 0000 0000 0000 0000 0000)
      4     4             (object header)                00 00 00 00 (0000 0000 0000 0000 0000 0000 0000 0000)
      8     4             (object header)                cb cf 00 20 (1100 1011 1100 1111 0000 0000 0010 0000)
     12     4         int Pattern.flags                  0
     16     4         int Pattern.capturingGroupCount    1
     20     4         int Pattern.localCount             0
     24     4         int Pattern.cursor                 48
     28     4         int Pattern.patternLength          0
     32     1     boolean Pattern.compiled               true
     33     1     boolean Pattern.hasSupplementary       false
     34     2             (alignment/padding gap)        N/A
     36     4      String Pattern.pattern                (object)
     40     4      String Pattern.normalizedPattern      (object)
     44     4        Node Pattern.root                   (object)
     48     4        Node Pattern.matchRoot              (object)
     52     4       int[] Pattern.buffer                 null
     56     4         Map Pattern.namedGroups            null
     60     4 GroupHead[] Pattern.groupNodes             null
     64     4       int[] Pattern.temp                   null
     68     4             (loss due to the next object alignment)
Instance size: 72 bytes (reported by Instrumentation API)
Space losses: 2 bytes internal + 4 bytes external = 6 bytes total

Vous pouvez obtenir une vue récapitulative de la taille profonde d'une instance d'objet à l'aide de GraphLayout.parseInstance(obj).toFootprint(). Bien sûr, certains objets de l'empreinte peuvent être partagés (également référencés à partir d'autres objets), il s'agit donc d'une surapproximation de l'espace qui pourrait être récupéré lorsque cet objet est collecté. Pour le résultat de Pattern.compile("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$") (pris à partir de cette réponse), jol signale une empreinte totale de 1840 octets, dont seulement 72 sont l'instance de Pattern elle-même.

java.util.regex.Pattern instance footprint:
     COUNT       AVG       SUM   DESCRIPTION
         1       112       112   [C
         3       272       816   [Z
         1        24        24   java.lang.String
         1        72        72   java.util.regex.Pattern
         9        24       216   java.util.regex.Pattern$1
        13        24       312   java.util.regex.Pattern$5
         1        16        16   java.util.regex.Pattern$Begin
         3        24        72   java.util.regex.Pattern$BitClass
         3        32        96   java.util.regex.Pattern$Curly
         1        24        24   java.util.regex.Pattern$Dollar
         1        16        16   java.util.regex.Pattern$LastNode
         1        16        16   java.util.regex.Pattern$Node
         2        24        48   java.util.regex.Pattern$Single
        40                1840   (total)

Si vous utilisez plutôt GraphLayout.parseInstance(obj).toPrintable(), jol vous dira l'adresse, la taille, le type, la valeur et le chemin des déréférences de champ à chaque objet référencé, bien que ce soit habituellement trop de détails pour être utile. Pour l'exemple de modèle en cours, vous pourriez obtenir ce qui suit. (Les adresses changeront probablement entre les exécutions.)

java.util.regex.Pattern object externals:
          ADDRESS       SIZE TYPE                             PATH                           VALUE
         d5e5f290         16 java.util.regex.Pattern$Node     .root.next.atom.next           (object)
         d5e5f2a0        120 (something else)                 (somewhere else)               (something else)
         d5e5f318         16 java.util.regex.Pattern$LastNode .root.next.next.next.next.next.next.next (object)
         d5e5f328      21664 (something else)                 (somewhere else)               (something else)
         d5e647c8         24 java.lang.String                 .pattern                       (object)
         d5e647e0        112 [C                               .pattern.value                 [^, [, a, -, z, A, -, Z, 0, -, 9, _, ., +, -, ], +, @, [, a, -, z, A, -, Z, 0, -, 9, -, ], +, \, ., [, a, -, z, A, -, Z, 0, -, 9, -, ., ], +, $]
         d5e64850        448 (something else)                 (somewhere else)               (something else)
         d5e64a10         72 java.util.regex.Pattern                                         (object)
         d5e64a58        416 (something else)                 (somewhere else)               (something else)
         d5e64bf8         16 java.util.regex.Pattern$Begin    .root                          (object)
         d5e64c08         24 java.util.regex.Pattern$BitClass .root.next.atom.val$rhs        (object)
         d5e64c20        272 [Z                               .root.next.atom.val$rhs.bits   [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
         d5e64d30         24 java.util.regex.Pattern$1        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e64d48         24 java.util.regex.Pattern$1        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs.val$rhs (object)
         d5e64d60         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e64d78         24 java.util.regex.Pattern$1        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$rhs (object)
         d5e64d90         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e64da8         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs.val$lhs (object)
         d5e64dc0         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs (object)
         d5e64dd8         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs        (object)
         d5e64df0         24 java.util.regex.Pattern$5        .root.next.atom                (object)
         d5e64e08         32 java.util.regex.Pattern$Curly    .root.next                     (object)
         d5e64e28         24 java.util.regex.Pattern$Single   .root.next.next                (object)
         d5e64e40         24 java.util.regex.Pattern$BitClass .root.next.next.next.atom.val$rhs (object)
         d5e64e58        272 [Z                               .root.next.next.next.atom.val$rhs.bits [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
         d5e64f68         24 java.util.regex.Pattern$1        .root.next.next.next.atom.val$lhs.val$lhs.val$lhs (object)
         d5e64f80         24 java.util.regex.Pattern$1        .root.next.next.next.atom.val$lhs.val$lhs.val$rhs (object)
         d5e64f98         24 java.util.regex.Pattern$5        .root.next.next.next.atom.val$lhs.val$lhs (object)
         d5e64fb0         24 java.util.regex.Pattern$1        .root.next.next.next.atom.val$lhs.val$rhs (object)
         d5e64fc8         24 java.util.regex.Pattern$5        .root.next.next.next.atom.val$lhs (object)
         d5e64fe0         24 java.util.regex.Pattern$5        .root.next.next.next.atom      (object)
         d5e64ff8         32 java.util.regex.Pattern$Curly    .root.next.next.next           (object)
         d5e65018         24 java.util.regex.Pattern$Single   .root.next.next.next.next      (object)
         d5e65030         24 java.util.regex.Pattern$BitClass .root.next.next.next.next.next.atom.val$rhs (object)
         d5e65048        272 [Z                               .root.next.next.next.next.next.atom.val$rhs.bits [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
         d5e65158         24 java.util.regex.Pattern$1        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e65170         24 java.util.regex.Pattern$1        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs.val$rhs (object)
         d5e65188         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs (object)
         d5e651a0         24 java.util.regex.Pattern$1        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$rhs (object)
         d5e651b8         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom.val$lhs.val$lhs (object)
         d5e651d0         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom.val$lhs (object)
         d5e651e8         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom (object)
         d5e65200         32 java.util.regex.Pattern$Curly    .root.next.next.next.next.next (object)
         d5e65220        120 (something else)                 (somewhere else)               (something else)
         d5e65298         24 java.util.regex.Pattern$Dollar   .root.next.next.next.next.next.next (object)

Les entrées "(autre chose)" décrire d'autres objets du tas qui ne font pas partie de ce graphe d'objets.

La meilleure documentation jol est la échantillons de jol dans le dépôt jol. Les exemples illustrent les opérations jol courantes et montrent comment utiliser jol pour analyser les composants internes des machines virtuelles et des garbage collector.


60
2018-05-04 00:46



Premièrement, "la taille d'un objet" n'est pas un concept bien défini en Java. Vous pourriez vouloir dire l'objet lui-même, avec seulement ses membres, l'objet et tous les objets auxquels il se réfère (le graphe de référence). Vous pourriez signifier la taille en mémoire ou la taille sur le disque. Et la JVM est autorisée à optimiser des choses comme des chaînes.

Donc, la seule façon correcte est de demander à la JVM, avec un bon profileur (j'utilise YourKit), ce qui n'est probablement pas ce que vous voulez.

Cependant, à partir de la description ci-dessus, il semble que chaque ligne soit autonome et ne possède pas un grand arbre de dépendance, donc la méthode de sérialisation sera probablement une bonne approximation sur la plupart des JVM. La façon la plus simple de le faire est la suivante:

 Serializable ser;
 ByteArrayOutputStream baos = new ByteArrayOutputStream();
 ObjectOutputStream oos = new ObjectOutputStream(baos);
 oos.writeObject(ser);
 oos.close();
 return baos.size();

Rappelez-vous que si vous avez des objets avec des références communes cette ne sera pas donne le bon résultat, et la taille de la sérialisation ne correspondra pas toujours à la taille en mémoire, mais c'est une bonne approximation. Le code sera un peu plus efficace si vous initialisez la taille ByteArrayOutputStream à une valeur sensible.


56
2017-09-09 17:22



J'ai accidentellement trouvé une classe Java "jdk.nashorn.internal.ir.debug.ObjectSizeCalculator", déjà en jdk, ce qui est facile à utiliser et semble très utile pour déterminer la taille d'un objet.

System.out.println(ObjectSizeCalculator.getObjectSize(new gnu.trove.map.hash.TObjectIntHashMap<String>(12000, 0.6f, -1)));
System.out.println(ObjectSizeCalculator.getObjectSize(new HashMap<String, Integer>(100000)));
System.out.println(ObjectSizeCalculator.getObjectSize(3));
System.out.println(ObjectSizeCalculator.getObjectSize(new int[]{1, 2, 3, 4, 5, 6, 7 }));
System.out.println(ObjectSizeCalculator.getObjectSize(new int[100]));

résultats:

164192
48
16
48
416

39
2017-09-09 07:53



Si vous voulez simplement savoir combien de mémoire est utilisée dans votre JVM, et combien est gratuit, vous pouvez essayer quelque chose comme ceci:

// Get current size of heap in bytes
long heapSize = Runtime.getRuntime().totalMemory();

// Get maximum size of heap in bytes. The heap cannot grow beyond this size.
// Any attempt will result in an OutOfMemoryException.
long heapMaxSize = Runtime.getRuntime().maxMemory();

// Get amount of free memory within the heap in bytes. This size will increase
// after garbage collection and decrease as new objects are created.
long heapFreeSize = Runtime.getRuntime().freeMemory();

edit: J'ai pensé que cela pourrait être utile car l'auteur de la question a également déclaré qu'il aimerait avoir une logique qui gère "lire autant de lignes que possible jusqu'à ce que j'aie utilisé 32 Mo de mémoire."


32
2017-09-09 17:19



Quand j'ai travaillé sur Twitter, j'ai écrit un utilitaire pour calculer la taille de l'objet profond. Il prend en compte différents modèles de mémoire (32 bits, compression oups, 64 bits), le remplissage, le remplissage des sous-classes, fonctionne correctement sur les structures de données circulaires et les tableaux. Vous pouvez simplement compiler ce fichier .java; il n'a pas de dépendances externes:

https://github.com/twitter/commons/blob/master/src/java/com/twitter/common/objectsize/ObjectSizeCalculator.java


17
2018-04-09 11:07



La plupart des autres réponses fournissent des tailles peu profondes - par ex. la taille d'un HashMap sans aucune des clés ou des valeurs, ce qui n'est pas probable ce que vous voulez.

Le projet jamm utilise le package java.lang.instrumentation ci-dessus, mais il marche dans l'arborescence et peut donc vous donner l'utilisation de la mémoire profonde.

new MemoryMeter().measureDeep(myHashMap);

https://github.com/jbellis/jamm


13
2018-03-06 14:07



Vous devez marcher les objets en utilisant la réflexion. Soyez prudent comme vous:

  • Le simple fait d'allouer un objet a une surcharge dans la machine virtuelle Java. La quantité varie en fonction de la machine virtuelle Java, de sorte que vous puissiez faire de cette valeur un paramètre. Au moins en faire une constante (8 octets?) Et appliquer à tout ce qui est alloué.
  • Juste parce que byte est théoriquement 1 octet ne signifie pas qu'il n'en faut qu'un en mémoire.
  • Il y aura des boucles dans les références d'objets, donc vous devrez garder un HashMap ou quelque chose en utilisant des objets-égaux comme le comparateur pour éliminer les boucles infinies.

@jodonnell: J'aime la simplicité de votre solution, mais beaucoup d'objets ne sont pas Serializable (donc cela lancerait une exception), les champs peuvent être transitoires, et les objets peuvent remplacer les méthodes standard.


9
2017-09-09 17:19