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


617

J'ai une application qui lit 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. Existe-t-il un moyen simple de connaître la taille de cet objet par programme? 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 du code qui dit lire jusqu'à 32 000 lignes , mais j'aimerais aussi avoir du code qui dit lire autant de lignes que possible jusqu'à ce que j'utilise 32 Mo de mémoire. C'est peut-être une question différente, mais j'aimerais quand même savoir.


J'ai ajouté mon agent avec les configurations mvn et expliqué comment ici: stackoverflow.com/a/36102269/711855
juanmf

Réponses:


460

Vous pouvez utiliser le package java.lang.instrument

Compilez et mettez cette classe dans un 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

2
@Stefan Nice hint! Pouvez - vous s'il vous plaît dites, quel sera la taille byte[0], byte[1],byte[5] , int[0], int[1], int[2]selon l'approche que vous avez décrite? Ce serait bien, si les résultats incluent une surcharge pour la longueur du tableau et l'alignement de la mémoire.
dma_k

8
J'ai essayé cela et j'ai obtenu des résultats étranges et inutiles. Les cordes étaient toujours 32, quelle que soit leur taille. Je pensais que c'était peut-être la taille du pointeur, mais pour une autre classe immuable que j'ai créée, j'en ai 24. Cela fonctionne bien pour les primitives, mais vous n'avez pas vraiment besoin d'un programme pour vous dire la taille d'un caractère.
Brel

6
@Brel cette solution n'est qu'une "approximation de la quantité de stockage consommée par l'objet spécifié", comme spécifié dans la documentation. Je suppose également que les auteurs ont décidé de définir la taille d'une chaîne sur 32 octets (uniquement le pointeur?) En raison du pool de chaînes Java, ce qui rend difficile de dire si une instance de chaîne est partagée (stockée dans le pool) ou local et unique à une classe.
Andrei I

11
Comment puis-je utiliser ObjectSizeFetcher, si je n'exporte pas le pot? J'ai testé un projet java dans eclipse.
Yura Shinkarev

3
@brel La raison pour laquelle une chaîne n'est que de 32 octets quelle que soit la longueur réelle est parce que la partie de longueur variable d'une chaîne est stockée dans un char [], qui est son propre objet. Pour obtenir la vraie taille d'un objet, vous devez ajouter la taille de lui-même et la taille de chaque objet auquel il fait référence.
tombrown52

117

Vous devez 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 machines virtuelles Java. Ces outils utilisent fortement Unsafe, JVMTI et Serviceability Agent (SA) pour décoder la disposition, l'empreinte et les références réelles des objets. 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 les tailles des primitives, des références et des éléments de tableau, utilisez VMSupport.vmDetails(). Sur Oracle JDK 1.8.0_40 exécuté sur 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 superficielle d'une instance d'objet en utilisant ClassLayout.parseClass(Foo.class).toPrintable()(en passant éventuellement une instance à toPrintable). Il s'agit uniquement de l'espace consommé par une seule instance de cette classe; il n'inclut aucun autre objet référencé par cette classe. Il ne comprend de tête VM pour l' en- tête d'objet, l' alignement du champ et le rembourrage. 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ée lorsque cet objet est récupéré. Pour le résultat de Pattern.compile("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$")(tiré de cette réponse ), jol rapporte 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 indiquera 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 généralement trop de détails pour être utile. Pour l'exemple de modèle en cours, vous pouvez obtenir les éléments suivants. (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écrivent d'autres objets du tas qui ne font pas partie de ce graphique d'objet .

La meilleure documentation jol est les exemples jol dans le référentiel jol. Les exemples illustrent les opérations courantes de jol et montrent comment utiliser jol pour analyser les composants internes de VM et de garbage collector.


18
Cette réponse devrait avoir plus de votes positifs. Certainement une très bonne option pour vérifier. EDIT: vérifié que cela a été ajouté cette année alors qu'une question a été posée en '08. Probablement l'option la meilleure et la plus simple pour faire ce que OP a demandé pour le moment.
loue

4
L'auteur de l'outil a écrit un article de blog sur Jol .
Mike

2
Pour déterminer la taille de l'objet "obj", utilisez: org.openjdk.jol.info.GraphLayout.parseInstance (obj) .totalSize ();
vigueur le

Notez que vmDetailsc'est maintenant VM.current().details().
Miha_x64

Découvrez que GraphLayout.parseInstance(instance).toFootprint()j'ai trouvé plus utile de comprendre la taille des objets
Mugen

82

J'ai accidentellement trouvé une classe java "jdk.nashorn.internal.ir.debug.ObjectSizeCalculator", déjà en jdk, 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

3
De même ici, j'essayais les autres solutions proposées ci-dessus et je suis tombé sur ObjectSizeCalculator. Je crois que personne ne l'a mentionné auparavant, car il a été récemment introduit sur le JDK 8 dans le cadre du projet Nashorn . Cependant, je n'ai trouvé aucune documentation officielle sur cette classe sur le Web.
Henrique Gontijo

Il ne semble pas tenir compte des longueurs de chaîne. S'agit-il seulement de la taille de la pile?
jontejj

1
J'ai un hashmap, où com.carrotsearch.RamUsageEstimator renvoie environ la moitié d'ObjectSizeCalculator. Laquelle est vraie? - Lequel est le plus fiable?
badera

9
Notez que ObjectSizeCalculatorn'est pris en charge que sur HotSpot VM
kellanburket

74

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 expliquent essentiellement la création d'une implémentation 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 récursivement un graphique d'objet pour calculer la taille totale.

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

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

PS 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 direct.


6
L'utilitaire sizeof est probablement le moyen le plus rapide. C'est essentiellement ce que Stefan a dit, mais déjà emballé dans un pot prêt à l'emploi.
Alexandre L Telles

62

Premièrement, "la taille d'un objet" n'est pas un concept bien défini en Java. Vous pourriez signifier l'objet lui-même, avec seulement ses membres, l'objet et tous les objets auxquels il fait référence (le graphique 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 les 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, d'après la description ci-dessus, il semble que chaque ligne sera autonome et n'aura pas une grande arborescence de dépendances, donc la méthode de sérialisation sera probablement une bonne approximation sur la plupart des machines virtuelles Java. La façon la plus simple de procéder est la suivante:

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

N'oubliez pas que si vous avez des objets avec des références communes, cela ne donnera pas le résultat correct 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.


2
J'aime cette approche. À quelle distance en termes de taille d'objet avez-vous été éloigné.
Berlin Brown

1
Très simple et efficace. D'autres méthodes sont trop compliquées (spécialement à l'intérieur d'Eclipse RCP). Merci.
marcolopes

19
La sérialisation ne suivra pas les variables transitoires, et la méthode de sérialisation par défaut écrit des chaînes en UTF-8, donc tous les caractères ANSI ne prendront qu'un octet. Si vous avez plusieurs cordes, votre taille sera si loin qu'elle sera inutile.
TMN

1
Bien que cela ne donne pas la taille exacte, pour mes besoins, je n'avais besoin que d'une comparaison entre 2 objets et SizeOf ne s'initialisera pas à partir d'une application Web. Merci!
Isaac

1
Bonne recommandation de YourKit . D'autres alternatives sont VirtualVM et jvmmonitor
angelcervera

38

Si vous souhaitez simplement savoir combien de mémoire est utilisée dans votre JVM, et combien est libre, 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'utilise 32 Mo de mémoire."


24
Ce n'est pas une bonne solution, car vous ne savez jamais quand un ramasse-miettes aura lieu, ni combien de mémoire supplémentaire sera allouée au tas à la fois.
Nick Fortescue

5
C'est vrai, et je ne voudrais pas que cela réponde à la question principale de ce message, mais cela pourrait l'aider à savoir par programme quand il est un peu près d'atteindre la taille maximale du tas.
mat le

1
Un autre problème de cette solution est lorsque vous êtes dans un environnement multi-thread (comme dans un serveur Web). Il est possible que d'autres threads soient en cours d'exécution et consomment de la mémoire. Avec cette approximation, vous calculez la mémoire utilisée dans toute la machine virtuelle.
angelcervera

1
Un autre inconvénient est que freeMemory renvoie une approximation. Essayez de créer un objet javax.crypto.Cipher. La différence entre deux appels à freeMemory (pour estimer la taille d'un chiffre) n'est pas constante!
Eugen

1
Je crois que vous pouvez forcer une collecte des ordures, vous pouvez donc faire des choses dans cette approche.
matanster

24

À l'époque où je travaillais sur Twitter, j'ai écrit un utilitaire pour calculer la taille d'un objet profond. Il prend en compte différents modèles de mémoire (32 bits, oups compressés, 64 bits), le remplissage, le remplissage de sous-classe, 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


1
Szia! Je voudrais également mentionner votre présentation : les diapositives 15 à 20 sont idéales pour vous faire une idée instinctive du coût de diverses décisions relatives à la structure des données. Merci d'avoir posté ça!
Luke Usherwood du

16
"il n'a pas de dépendances externes" - depuis quand la goyave n'est-elle pas une dépendance externe?
l4mpi


Guave est une dépendance externe.
Mert Serimer

18

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

Le projet jamm utilise le package java.lang.instrumentation ci-dessus mais parcourt l'arborescence et peut donc vous donner une utilisation approfondie de la mémoire.

new MemoryMeter().measureDeep(myHashMap);

https://github.com/jbellis/jamm

Pour utiliser MemoryMeter, démarrez la JVM avec "-javaagent: /jamm.jar"


11

Vous devez parcourir les objets en utilisant la réflexion. Soyez prudent lorsque vous faites:

  • Le simple fait d'allouer un objet a une surcharge dans la JVM. Le montant varie en fonction de la machine virtuelle Java, vous pouvez donc faire de cette valeur un paramètre. Faites-en au moins une constante (8 octets?) Et appliquez-vous à tout ce qui est alloué.
  • Tout simplement parce que byte n'est pas est théoriquement 1 octet qu'il ne prend qu'un seul en mémoire.
  • Il y aura des boucles dans les références aux objets, vous devrez donc conserver un HashMapou un peu tel en utilisant object-equals comme comparateur pour éliminer les boucles infinies.

@jodonnell: J'aime la simplicité de votre solution, mais de nombreux objets ne sont pas sérialisables (donc cela lèverait une exception), les champs peuvent être transitoires et les objets peuvent remplacer les méthodes standard.


Les tailles des différentes primitives ne sont-elles pas définies dans la spécification Java? (§2.4.1)
erickson

4
Pas dans le sens de «combien de mémoire occupe-t-il», telle est la question. Seulement dans le sens de leur fonctionnement. Par exemple, les octets, les caractères et les shorts occupent un mot entier sur la pile Java, même s'ils fonctionnent avec des arrondis, etc.
Jason Cohen

1
Cela ressemble à la mesure de la taille, comme le montre Heinz dans sa Newsletter # 78: javaspecialists.eu/archive/Issue078.html . Je l'ai utilisé. Son approche fonctionne.
Peter Kofler

8

Vous devez le mesurer avec un outil, ou l'estimer à la main, et cela dépend de la JVM que vous utilisez.

Il y a une surcharge fixe par objet. Il est spécifique à JVM, mais j'évalue généralement 40 octets. Ensuite, vous devez regarder les membres de la classe. Les références d'objet sont de 4 (8) octets dans une machine virtuelle Java 32 bits (64 bits). Les types primitifs sont:

  • booléen et octet: 1 octet
  • char et short: 2 octets
  • int et float: 4 octets
  • long et double: 8 octets

Les tableaux suivent les mêmes règles; c'est-à-dire qu'il s'agit d'une référence d'objet qui prend donc 4 (ou 8) octets dans votre objet, puis sa longueur multipliée par la taille de son élément.

Essayer de le faire par programme avec des appels à Runtime.freeMemory()ne vous donne pas beaucoup de précision, en raison d'appels asynchrones au garbage collector, etc. Le profilage du tas avec -Xrunhprof ou d'autres outils vous donnera les résultats les plus précis.


@erickson Je ne serais pas sûr de sizeof (boolean) == 1 en regardant ce fil ( stackoverflow.com/questions/1907318/… ). Pouvez-vous commenter cela?
dma_k

2
@dma_k, Java n'a pas vraiment de vrais booléens. La taille du booléen est de 4 octets à l'extérieur des tableaux et de 1 octet à l'intérieurboolean[] . En fait, toutes les primitives de type non double / long font 4 octets. Ces derniers sont 8 (la réponse les met à tort comme 4 aussi)
bestsss

@bestsss: Pour être plus précis, l'allocation de mémoire minimale dépend de la plate-forme et de l'implémentation de JVM. Les objets sur le tas sont également alignés, donc après avoir résumé toutes les tailles, vous devez arrondir.
dma_k

6

La java.lang.instrument.Instrumentationclasse fournit un bon moyen d'obtenir la taille d'un objet Java, mais elle vous oblige à définir un premainet à exécuter votre programme avec un agent java. Cela est très ennuyeux lorsque vous n'avez besoin d'aucun agent et que vous devez ensuite fournir un agent factice Jar à votre application.

J'ai donc obtenu une solution alternative en utilisant la Unsafeclasse du sun.misc. Ainsi, en considérant l'alignement du tas d'objets selon l'architecture du processeur et le calcul du décalage de champ maximal, vous pouvez mesurer la taille d'un objet Java. Dans l'exemple ci-dessous, j'utilise une classe auxiliaire UtilUnsafepour obtenir une référence à l' sun.misc.Unsafeobjet.

private static final int NR_BITS = Integer.valueOf(System.getProperty("sun.arch.data.model"));
private static final int BYTE = 8;
private static final int WORD = NR_BITS/BYTE;
private static final int MIN_SIZE = 16; 

public static int sizeOf(Class src){
    //
    // Get the instance fields of src class
    // 
    List<Field> instanceFields = new LinkedList<Field>();
    do{
        if(src == Object.class) return MIN_SIZE;
        for (Field f : src.getDeclaredFields()) {
            if((f.getModifiers() & Modifier.STATIC) == 0){
                instanceFields.add(f);
            }
        }
        src = src.getSuperclass();
    }while(instanceFields.isEmpty());
    //
    // Get the field with the maximum offset
    //  
    long maxOffset = 0;
    for (Field f : instanceFields) {
        long offset = UtilUnsafe.UNSAFE.objectFieldOffset(f);
        if(offset > maxOffset) maxOffset = offset; 
    }
    return  (((int)maxOffset/WORD) + 1)*WORD; 
}
class UtilUnsafe {
    public static final sun.misc.Unsafe UNSAFE;

    static {
        Object theUnsafe = null;
        Exception exception = null;
        try {
            Class<?> uc = Class.forName("sun.misc.Unsafe");
            Field f = uc.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            theUnsafe = f.get(uc);
        } catch (Exception e) { exception = e; }
        UNSAFE = (sun.misc.Unsafe) theUnsafe;
        if (UNSAFE == null) throw new Error("Could not obtain access to sun.misc.Unsafe", exception);
    }
    private UtilUnsafe() { }
}

Approche intéressante, mais cela ne suppose-t-il pas que l'objet et son stockage de champs ne sont pas fragmentés?
nicoulaj

Oui et je ne connais aucune implémentation JVM qui rend une telle fragmentation.
Miguel Gamboa

Je ne comprends pas. La fragmentation n'est pas une option :) Prenons l'exemple de l'objet C qui est stocké en tant que champ d'objets A et B. Ne déplace-t-il pas le tout dans A ou B?
nicoulaj

Désolé, mais je ne comprends pas non plus votre point de vue. Selon mon interprétation, en Java, les objets ne peuvent pas être stockés dans d'autres objets, comme cela se produit avec les structures C ou les types de valeur dans .Net. Donc, quand vous dites: "objet C qui est stocké comme un champ d'objets A et B" cela signifie que les objets A et B ont des champs qui stockent des références (pointeurs) à l'objet C. Ensuite, la taille de A et B est égale à l'offset de ce champ plus la taille d'une référence (pointeur) à l'objet C. Et la taille d'une référence est la taille d'un mot.
Miguel Gamboa

Oh, OK, nous parlons de taille peu profonde. Ma faute.
nicoulaj

6

Il existe également l' outil Memory Measurer (anciennement chez Google Code , maintenant sur GitHub ), qui est simple et publié sous la licence commerciale Apache 2.0 , comme discuté dans une question similaire .

Il nécessite également un argument de ligne de commande à l'interpréteur java si vous voulez mesurer la consommation d'octets de mémoire, mais semble fonctionner correctement, au moins dans les scénarios que je l'ai utilisés.


4

Sans avoir à vous soucier de l'instrumentation et ainsi de suite, et si vous n'avez pas besoin de connaître la taille exacte en octets d'un objet, vous pouvez opter pour l'approche suivante:

System.gc();
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

do your job here

System.gc();
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

De cette façon, vous lisez la mémoire utilisée avant et après, et en appelant le GC juste avant d'obtenir la mémoire utilisée, vous réduisez le "bruit" presque à 0.

Pour un résultat plus fiable, vous pouvez exécuter votre travail n fois, puis diviser la mémoire utilisée par n, en obtenant la quantité de mémoire nécessaire pour une exécution. Encore plus, vous pouvez exécuter le tout plusieurs fois et faire une moyenne.


5
Ne vous System.gc()informe- t-il pas simplement que vous souhaitez vous adresser au GC? Il n'est pas garanti que le GC soit appelé du tout.
Raildex

@vraiment sympa. Ce n'est pas sûr car vous ne savez peut-être jamais ce que GC fait ou affecte la mémoire entre vos lignes. Donc, "entre" deux méthodes freeMemory, GC peut libérer plus d'espace que vous ne considérez pas, donc votre objet paraîtra plus petit
Mert Serimer

@MertSerimer "pas sûr" est à un niveau complètement différent pour moi: tout au plus ce n'est pas tellement précis, comme je l'ai également dit. De plus, vous ne pouvez pas conduire le GC (comme l'a déclaré Raildex), mais dans ce cas aussi, j'ai suggéré de l'insérer dans un cycle. C'est juste un système rapide, sale et approximatif qui fonctionne si le résultat n'a pas besoin d'être très fiable, comme indiqué.
reallynice

Il y a beaucoup de problèmes avec cela, mais cela vous donne un bon butin.
markthegrea

3

Voici un utilitaire que j'ai créé en utilisant certains des exemples liés pour gérer les bits 32, 64 et 64 bits avec la POO compressée. Il utilise sun.misc.Unsafe.

Il utilise Unsafe.addressSize()pour obtenir la taille d'un pointeur natif et Unsafe.arrayIndexScale( Object[].class )pour la taille d'une référence Java.

Il utilise le décalage de champ d'une classe connue pour déterminer la taille de base d'un objet.

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.IdentityHashMap;
import java.util.Stack;
import sun.misc.Unsafe;

/** Usage: 
 * MemoryUtil.sizeOf( object )
 * MemoryUtil.deepSizeOf( object )
 * MemoryUtil.ADDRESS_MODE
 */
public class MemoryUtil
{
    private MemoryUtil()
    {
    }

    public static enum AddressMode
    {
        /** Unknown address mode. Size calculations may be unreliable. */
        UNKNOWN,
        /** 32-bit address mode using 32-bit references. */
        MEM_32BIT,
        /** 64-bit address mode using 64-bit references. */
        MEM_64BIT,
        /** 64-bit address mode using 32-bit compressed references. */
        MEM_64BIT_COMPRESSED_OOPS
    }

    /** The detected runtime address mode. */
    public static final AddressMode ADDRESS_MODE;

    private static final Unsafe UNSAFE;

    private static final long ADDRESS_SIZE; // The size in bytes of a native pointer: 4 for 32 bit, 8 for 64 bit
    private static final long REFERENCE_SIZE; // The size of a Java reference: 4 for 32 bit, 4 for 64 bit compressed oops, 8 for 64 bit
    private static final long OBJECT_BASE_SIZE; // The minimum size of an Object: 8 for 32 bit, 12 for 64 bit compressed oops, 16 for 64 bit
    private static final long OBJECT_ALIGNMENT = 8;

    /** Use the offset of a known field to determine the minimum size of an object. */
    private static final Object HELPER_OBJECT = new Object() { byte b; };


    static
    {
        try
        {
            // Use reflection to get a reference to the 'Unsafe' object.
            Field f = Unsafe.class.getDeclaredField( "theUnsafe" );
            f.setAccessible( true );
            UNSAFE = (Unsafe) f.get( null );

            OBJECT_BASE_SIZE = UNSAFE.objectFieldOffset( HELPER_OBJECT.getClass().getDeclaredField( "b" ) );

            ADDRESS_SIZE = UNSAFE.addressSize();
            REFERENCE_SIZE = UNSAFE.arrayIndexScale( Object[].class );

            if( ADDRESS_SIZE == 4 )
            {
                ADDRESS_MODE = AddressMode.MEM_32BIT;
            }
            else if( ADDRESS_SIZE == 8 && REFERENCE_SIZE == 8 )
            {
                ADDRESS_MODE = AddressMode.MEM_64BIT;
            }
            else if( ADDRESS_SIZE == 8 && REFERENCE_SIZE == 4 )
            {
                ADDRESS_MODE = AddressMode.MEM_64BIT_COMPRESSED_OOPS;
            }
            else
            {
                ADDRESS_MODE = AddressMode.UNKNOWN;
            }
        }
        catch( Exception e )
        {
            throw new Error( e );
        }
    }


    /** Return the size of the object excluding any referenced objects. */
    public static long shallowSizeOf( final Object object )
    {
        Class<?> objectClass = object.getClass();
        if( objectClass.isArray() )
        {
            // Array size is base offset + length * element size
            long size = UNSAFE.arrayBaseOffset( objectClass )
                    + UNSAFE.arrayIndexScale( objectClass ) * Array.getLength( object );
            return padSize( size );
        }
        else
        {
            // Object size is the largest field offset padded out to 8 bytes
            long size = OBJECT_BASE_SIZE;
            do
            {
                for( Field field : objectClass.getDeclaredFields() )
                {
                    if( (field.getModifiers() & Modifier.STATIC) == 0 )
                    {
                        long offset = UNSAFE.objectFieldOffset( field );
                        if( offset >= size )
                        {
                            size = offset + 1; // Field size is between 1 and PAD_SIZE bytes. Padding will round up to padding size.
                        }
                    }
                }
                objectClass = objectClass.getSuperclass();
            }
            while( objectClass != null );

            return padSize( size );
        }
    }


    private static final long padSize( final long size )
    {
        return (size + (OBJECT_ALIGNMENT - 1)) & ~(OBJECT_ALIGNMENT - 1);
    }


    /** Return the size of the object including any referenced objects. */
    public static long deepSizeOf( final Object object )
    {
        IdentityHashMap<Object,Object> visited = new IdentityHashMap<Object,Object>();
        Stack<Object> stack = new Stack<Object>();
        if( object != null ) stack.push( object );

        long size = 0;
        while( !stack.isEmpty() )
        {
            size += internalSizeOf( stack.pop(), stack, visited );
        }
        return size;
    }


    private static long internalSizeOf( final Object object, final Stack<Object> stack, final IdentityHashMap<Object,Object> visited )
    {
        // Scan for object references and add to stack
        Class<?> c = object.getClass();
        if( c.isArray() && !c.getComponentType().isPrimitive() )
        {
            // Add unseen array elements to stack
            for( int i = Array.getLength( object ) - 1; i >= 0; i-- )
            {
                Object val = Array.get( object, i );
                if( val != null && visited.put( val, val ) == null )
                {
                    stack.add( val );
                }
            }
        }
        else
        {
            // Add unseen object references to the stack
            for( ; c != null; c = c.getSuperclass() )
            {
                for( Field field : c.getDeclaredFields() )
                {
                    if( (field.getModifiers() & Modifier.STATIC) == 0 
                            && !field.getType().isPrimitive() )
                    {
                        field.setAccessible( true );
                        try
                        {
                            Object val = field.get( object );
                            if( val != null && visited.put( val, val ) == null )
                            {
                                stack.add( val );
                            }
                        }
                        catch( IllegalArgumentException e )
                        {
                            throw new RuntimeException( e );
                        }
                        catch( IllegalAccessException e )
                        {
                            throw new RuntimeException( e );
                        }
                    }
                }
            }
        }

        return shallowSizeOf( object );
    }
}

Avez-vous testé cette classe avec des valeurs? J'ai essayé, mais pour moi, des valeurs incorrectes !!!.
Débora

1
Les valeurs qu'il m'a données pour un objet simple étaient à peu près correctes, mais décalées d'un facteur 10 pour une liste contenant 1 million d'objets. Encore un très beau travail!
Michael Böckling

Intéressant. Je l'ai testé en utilisant JDK7u67, sur Windows 7 x64 et Linux 2.6.16 / x86_64, en utilisant chacun des modes d'adresse 32 bits / 64 bits / oop. Je l'ai comparé aux vidages de mémoire analysés dans Eclipse Memory Analyzer 1.3.x. Quelle configuration utilisez-vous? Avez-vous un exemple spécifique que je pourrais essayer?
dlaudams

Le meilleur choix que je puisse faire. Je ne peux pas l'utiliser Instrumentationparce que je ne démarre pas tomcat, ObjectSizeCalculatorcar je ne suis pas sûr du type de machine virtuelle (HotSpot) et des JOLharicots de printemps bacouse. J'utilise ceci et j'ajoute un deuxième paramètre pour ignorer les singletons à savoir AbstractRefreshableApplicationContext.getBeanFactory().getSingletonMutex()et le internalSizeOfcode de refactor pour ignorer la classe et l'
énumération

Pour comparer les résultats, utilisez ObjectSizeCalculator (calculez le serveur entier de 1 Go à 10 s). JOL cause MemError (6 Go ne doivent pas être capturés) et je n'obtiens pas les mêmes résultats, probablement à cause des énumérations.
Perlos

3

Je cherchais un calcul d'exécution d'une taille d'objet qui répondait aux exigences suivantes:

  • Disponible à l'exécution sans avoir besoin d'inclure l'instrumentation.
  • Fonctionne avec Java 9+ sans accès à Unsafe.
  • Est basé sur la classe uniquement. Pas une taille profonde qui prend en compte les longueurs de chaîne, les longueurs de tableau, etc.

Ce qui suit est basé sur le code de base de l'article original des spécialistes java ( https://www.javaspecialists.eu/archive/Issue078.html ) et quelques bits de la version Unsafe dans une autre réponse à cette question.

J'espère que quelqu'un le trouvera utile.

public class JavaSize {

private static final int NR_BITS = Integer.valueOf(System.getProperty("sun.arch.data.model"));
private static final int BYTE = 8;
private static final int WORD = NR_BITS / BYTE;
private static final int HEADER_SIZE = 8;

public static int sizeOf(Class<?> clazz) {
    int result = 0;

    while (clazz != null) {
        Field[] fields = clazz.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            if (!Modifier.isStatic(fields[i].getModifiers())) {
                if (fields[i].getType().isPrimitive()) {
                    Class<?> primitiveClass = fields[i].getType();
                    if (primitiveClass == boolean.class || primitiveClass == byte.class) {
                        result += 1;
                    } else if (primitiveClass == short.class) {
                        result += 2;
                    } else if (primitiveClass == int.class || primitiveClass == float.class) {
                        result += 4;
                    } else if (primitiveClass == double.class || primitiveClass == long.class) {
                        result += 8;
                    }

                } else {
                    // assume compressed references.
                    result += 4;
                }
            }
        }

        clazz = clazz.getSuperclass();

        // round up to the nearest WORD length.
        if ((result % WORD) != 0) {
            result += WORD - (result % WORD);
        }
    }

    result += HEADER_SIZE;

    return result;
}

}


2

Il n'y a pas d'appel de méthode, si c'est ce que vous demandez. Avec un peu de recherche, je suppose que vous pourriez écrire le vôtre. Une instance particulière a une taille fixe dérivée du nombre de références et de valeurs primitives plus les données de tenue de livres d'instance. Vous parcourriez simplement le graphique d'objet. Moins les types de lignes sont variés, plus c'est facile.

Si c'est trop lent ou juste plus de problèmes que ça en vaut la peine, il y a toujours une bonne règle de comptage de lignes à l'ancienne.


2

J'ai écrit une fois un test rapide pour estimer à la volée:

public class Test1 {

    // non-static nested
    class Nested { }

    // static nested
    static class StaticNested { }

    static long getFreeMemory () {
        // waits for free memory measurement to stabilize
        long init = Runtime.getRuntime().freeMemory(), init2;
        int count = 0;
        do {
            System.out.println("waiting..." + init);
            System.gc();
            try { Thread.sleep(250); } catch (Exception x) { }
            init2 = init;
            init = Runtime.getRuntime().freeMemory();
            if (init == init2) ++ count; else count = 0;
        } while (count < 5);
        System.out.println("ok..." + init);
        return init;
    }

    Test1 () throws InterruptedException {

        Object[] s = new Object[10000];
        Object[] n = new Object[10000];
        Object[] t = new Object[10000];

        long init = getFreeMemory();

        //for (int j = 0; j < 10000; ++ j)
        //    s[j] = new Separate();

        long afters = getFreeMemory();

        for (int j = 0; j < 10000; ++ j)
            n[j] = new Nested();

        long aftersn = getFreeMemory();

        for (int j = 0; j < 10000; ++ j)
            t[j] = new StaticNested();

        long aftersnt = getFreeMemory();

        System.out.println("separate:      " + -(afters - init) + " each=" + -(afters - init) / 10000);
        System.out.println("nested:        " + -(aftersn - afters) + " each=" + -(aftersn - afters) / 10000);
        System.out.println("static nested: " + -(aftersnt - aftersn) + " each=" + -(aftersnt - aftersn) / 10000);

    }

    public static void main (String[] args) throws InterruptedException {
        new Test1();
    }

}

Le concept général consiste à allouer des objets et à mesurer les changements dans l'espace de mémoire libre. La clé étant getFreeMemory(), ce qui demande exécute GC et attend la taille de tas libre rapporté à se stabiliser . La sortie de ce qui précède est:

nested:        160000 each=16
static nested: 160000 each=16

C'est ce que nous attendons, compte tenu du comportement d'alignement et de la surcharge possible de l'en-tête du bloc de tas.

La méthode d'instrumentation détaillée dans la réponse acceptée ici est la plus précise. La méthode que j'ai décrite est précise mais uniquement dans des conditions contrôlées où aucun autre thread ne crée / supprime des objets.


2

Utilisez simplement Java Visual VM.

Il a tout ce dont vous avez besoin pour profiler et déboguer les problèmes de mémoire.

Il dispose également d'une console OQL (Object Query Language) qui vous permet de faire beaucoup de choses utiles, dont l'une étant sizeof(o)


2

Lorsque vous utilisez JetBrains IntelliJ, activez d'abord "Attach memory agent" dans File | Paramètres | Construction, exécution, déploiement | Débogueur.

Lors du débogage, cliquez avec le bouton droit sur une variable d'intérêt et choisissez «Calculer la taille retenue»: Calculer la taille retenue


1

Ma réponse est basée sur le code fourni par Nick. Ce code mesure la quantité totale d'octets qui sont occupés par l'objet sérialisé. Donc, cela mesure réellement les éléments de sérialisation + l'empreinte de la mémoire des objets simples (sérialisez simplement par exemple intet vous verrez que la quantité totale d'octets sérialisés ne l'est pas 4). Donc, si vous voulez que le numéro d'octet brut soit utilisé exactement pour votre objet - vous devez modifier un peu ce code. Ainsi:

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class ObjectSizeCalculator {
    private Object getFirstObjectReference(Object o) {
        String objectType = o.getClass().getTypeName();

        if (objectType.substring(objectType.length()-2).equals("[]")) {
            try {
                if (objectType.equals("java.lang.Object[]"))
                    return ((Object[])o)[0];
                else if (objectType.equals("int[]"))
                    return ((int[])o)[0];
                else
                    throw new RuntimeException("Not Implemented !");
            } catch (IndexOutOfBoundsException e) {
                return null;
            }
        }

        return o;
    } 

    public int getObjectSizeInBytes(Object o) {
        final String STRING_JAVA_TYPE_NAME = "java.lang.String";

        if (o == null)
            return 0;

        String objectType = o.getClass().getTypeName();
        boolean isArray = objectType.substring(objectType.length()-2).equals("[]");

        Object objRef = getFirstObjectReference(o);
        if (objRef != null && !(objRef instanceof Serializable))
            throw new RuntimeException("Object must be serializable for measuring it's memory footprint using this method !");

        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(o);
            oos.close();
            byte[] bytes = baos.toByteArray();

            for (int i = bytes.length - 1, j = 0; i != 0; i--, j++) {
                if (objectType != STRING_JAVA_TYPE_NAME) {
                    if (bytes[i] == 112)
                        if (isArray)
                            return j - 4;
                        else
                            return j;
                } else {
                    if (bytes[i] == 0)
                        return j - 1;
                }
            }
        } catch (Exception e) {
            return -1;
        }

        return -1;
    }    

}

J'ai testé cette solution avec des types primitifs, String et sur certaines classes triviales. Il peut également ne pas y avoir de cas couverts.


MISE À JOUR: Exemple modifié pour prendre en charge le calcul de l'empreinte mémoire des objets du tableau.


0

Vous pouvez générer un vidage de tas (avec jmap, par exemple), puis analyser la sortie pour trouver la taille des objets. Il s'agit d'une solution hors ligne, mais vous pouvez examiner les tailles peu profondes et profondes, etc.


0
long heapSizeBefore = Runtime.getRuntime().totalMemory();

// Code for object construction
...
long heapSizeAfter = Runtime.getRuntime().totalMemory();
long size = heapSizeAfter - heapSizeBefore;

size vous donne l'augmentation de l'utilisation de la mémoire du jvm en raison de la création d'objet et c'est généralement la taille de l'objet.


que faire si GC s'exécute au milieu pendant // Code pour la construction d'objets? Peut maintenant donner un résultat correct tout le temps.
rajugaadu

0

Cette réponse n'est pas liée à la taille de l'objet, mais lorsque vous utilisez un tableau pour héberger les objets; la quantité de mémoire qu'il allouera à l'objet.

Ainsi, les tableaux, la liste ou la carte de toutes ces collections ne stockeront pas vraiment les objets (uniquement au moment des primitives, la taille réelle de la mémoire des objets est nécessaire), elle ne stockera que les références de ces objets.

Maintenant le Used heap memory = sizeOfObj + sizeOfRef (* 4 bytes) in collection

  • (4/8 octets) dépend du système d'exploitation (32/64 bits)

PRIMITIVES

int   [] intArray    = new int   [1]; will require 4 bytes.
long  [] longArray   = new long  [1]; will require 8 bytes.

OBJETS

Object[] objectArray = new Object[1]; will require 4 bytes. The object can be any user defined Object.
Long  [] longArray   = new Long  [1]; will require 4 bytes.

Je veux dire que tout l'objet REFERENCE n'a besoin que de 4 octets de mémoire. Il peut s'agir d'une référence de chaîne OU d'une référence d'objet double, mais cela dépend de la création de l'objet, la mémoire nécessaire variera.

eg) Si je crée un objet pour la classe ci-dessous, ReferenceMemoryTestalors 4 + 4 + 4 = 12 octets de mémoire seront créés. La mémoire peut différer lorsque vous essayez d'initialiser les références.

 class ReferenceMemoryTest {
    public String refStr;
    public Object refObj;
    public Double refDoub; 
}

Ainsi, lors de la création d'un tableau objet / référence, tout son contenu sera occupé par des références NULL. Et nous savons que chaque référence nécessite 4 octets.

Et enfin, l'allocation de mémoire pour le code ci-dessous est de 20 octets.

ReferenceMemoryTest ref1 = new ReferenceMemoryTest (); (4 (ref1) + 12 = 16 octets) ReferenceMemoryTest ref2 = ref1; (4 (ref2) + 16 = 20 octets)


1
Comment un entier de 4 octets et une référence d'objet de taille inconnue peuvent-ils tenir dans 4 octets?
Marquis de Lorne

@EJP Je veux dire que tout l'objet REFERENCE n'a besoin que de 4 octets de mémoire. Il peut s'agir d'une référence de chaîne OU d'une référence d'objet double, mais cela dépend de la création de l'objet, la mémoire nécessaire variera.
Kanagavelu Sugumar

0

Supposons que je déclare une classe nommée Complexcomme:

public class Complex {

    private final long real;
    private final long imaginary;

    // omitted
}

Afin de voir combien de mémoire est allouée aux instances actives de cette classe:

$ jmap -histo:live <pid> | grep Complex

 num     #instances         #bytes  class name (module)
-------------------------------------------------------
 327:             1             32  Complex

-5

Pour JSONObject, le code ci-dessous peut vous aider.

`JSONObject.toString().getBytes("UTF-8").length`

renvoie la taille en octets

Je l'ai vérifié avec mon objet JSONArray en l'écrivant dans un fichier. Il donne la taille d'un objet.


cela ne fonctionnerait que pour les objets qui sont principalement des chaînes.
Dexter Legaspi

-6

Je doute que vous souhaitiez le faire par programme, sauf si vous voulez le faire une seule fois et le stocker pour une utilisation future. C'est une chose coûteuse à faire. Il n'y a pas d'opérateur sizeof () en Java, et même s'il y en avait un, il ne compterait que le coût des références à d'autres objets et la taille des primitives.

Une façon de le faire est de sérialiser la chose dans un fichier et de regarder la taille du fichier, comme ceci:

Serializable myObject;
ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("obj.ser"));
oos.write (myObject);
oos.close ();

Bien sûr, cela suppose que chaque objet est distinct et ne contient pas de références non transitoires à autre chose.

Une autre stratégie serait de prendre chaque objet et d'examiner ses membres par réflexion et d'additionner les tailles (booléen & octet = 1 octet, court & char = 2 octets, etc.), en descendant dans la hiérarchie des membres. Mais c'est fastidieux et coûteux et finit par faire la même chose que la stratégie de sérialisation.


3
Je le sérialiserais en octet [] en utilisant un ByteArrayOutputStream. Ce serait beaucoup plus rapide que de l'écrire dans un fichier.
ScArcher2

@KorayTugay La détermination de la taille en octets d'un objet est déjà une opération coûteuse. Écrire chaque objet sur le disque pour déterminer la taille, va juste le faire ramper ...
HammerNL

1
Le format d'objet sérialisé est entièrement différent du format de l'objet dans la mémoire de tas. Plus particulièrement, un descripteur pour la classe de l'objet (et toutes ses superclasses sérialisables) est écrit dans le flux. Ainsi, l'écriture d'une simple instance de java.lang.Integerproduit environ 80 octets, où la représentation en tas est généralement de 32 (contrairement à la représentation en flux d'objets, la représentation en tas dépend de la taille des pointeurs et de l'alignement des objets). En revanche, une nullréférence sérialisée nécessite un octet au lieu des quatre ou huit octets dans la mémoire de tas.
Holger
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.