Comment obtenir la taille d’un objet en Java

1. Vue d’ensemble

Contrairement à C/C ++ où nous pouvons utiliser la méthode sizeof _ () _ pour obtenir une taille d’objet en octets, il n’existe pas de véritable équivalent de cette méthode en Java.

Dans cet article, nous montrerons comment nous pouvons toujours obtenir la taille d’un objet particulier.

2. Consommation de mémoire en Java

Bien qu’il n’y ait pas d’opérateur sizeof en Java, nous n’en avons pas besoin. Tous les types primitifs ont une taille standard et il n’ya généralement pas d’octets pad ou d’alignement. Pourtant, ce n’est pas toujours simple.

  • Bien que les primitives doivent se comporter comme si elles avaient les tailles officielles, une machine virtuelle Java peut stocker les données de la manière qui lui convient le mieux en interne, avec n’importe quelle quantité de remplissage ou de surcharge ** . Il peut choisir de stocker un boolean[] ​​dans des morceaux longs de 64 bits comme BitSet , d’allouer des __Object __s temporaires sur la pile ou d’optimiser certaines variables ou appels de méthodes totalement obsolètes, en les remplaçant par des constantes, etc. comme le programme donne le même résultat, tout va bien.

Si l’on prend également en compte l’impact des caches de matériel et de système d’exploitation du compte (nos données peuvent être dupliquées à chaque niveau de cache), cela signifie que nous ne pouvons prédire approximativement la consommation de RAM .

2.1. Objets, références et classes wrapper

  • La taille minimale de l’objet est de 16 octets pour le JDK 64 bits ** moderne, car l’objet a un en-tête de 12 octets, complété d’un multiple de 8 octets. Dans le JDK 32 bits, la surcharge est de 8 octets, complétée par un multiple de 4 octets.

  • Les références ont une taille typique de 4 octets sur les plateformes 32 bits et 64 bits ** avec une limite de segment de mémoire inférieure à 32 Go ( -Xmx32G ) et de 8 octets pour cette limite supérieure à 32 Go.

Cela signifie qu’une machine virtuelle Java 64 bits nécessite généralement 30 à 50% d’espace supplémentaire.

Il est particulièrement important de noter que les types encadrés, les tableaux, les _String s et les autres conteneurs tels que les tableaux multidimensionnels coûtent cher en mémoire car ils ajoutent un surcoût . Par exemple, lorsque nous comparons int primitive (qui ne consomme que 4 octets) à l’objet Integer_ qui prend 16 octets, nous voyons qu’il existe une surcharge de mémoire de 300%.

3. Estimation de la taille d’un objet à l’aide d’instruments

Une façon d’obtenir une estimation de la taille d’un objet en Java consiste à utiliser la méthode getObjectSize (Object) de la https://docs.oracle.com/javase/7/docs/api/java/lang/instrument/Instrumentation.html .[ Instrumentation interface]introduit en Java 5.

Comme nous avons pu le constater dans la documentation Javadoc, la méthode fournit une «approximation spécifique à l’implémentation» de la taille de l’objet spécifié.

Il convient de noter qu’une surcharge potentielle peut être incluse dans la taille et que les valeurs peuvent être différentes lors de l’appel d’une machine virtuelle Java unique.

  • Cette approche ne prend en charge que l’estimation de la taille de l’objet considéré lui-même et non la taille des objets qu’il référence ** . Pour estimer la taille totale de l’objet, nous aurions besoin d’un code qui passerait en revue ces références et calculera la taille estimée.

3.1. Création d’un agent d’instrumentation

Pour appeler Instrumentation.getObjectSize (Object) afin d’obtenir la taille de l’objet, vous devez d’abord pouvoir accéder à l’instance d’Instrumentation. Nous devons utiliser l’agent d’instrumentation et il existe deux façons de le faire, comme indiqué dans la documentation de https://docs.oracle.com/javase/7/docs/api/java/lang/instrument/package . -summary.html[paquetage java.lang.instrument .]

  • L’agent d’instrumentation peut être spécifié via la ligne de commande ou peut être utilisé avec une JVM ** déjà en cours d’exécution. Nous allons nous concentrer sur le premier.

Pour spécifier l’agent d’instrumentation via la ligne de commande , nous aurons besoin de l’implémentation de la méthode surchargée premain qui sera d’abord appelée par la JVM lors de l’utilisation de l’instrumentation. De plus, nous devons exposer une méthode statique pour pouvoir accéder à Instrumentation.getObjectSize (Object) .

Créons maintenant la classe InstrumentationAgent :

public class InstrumentationAgent {
    private static volatile Instrumentation globalInstrumentation;

    public static void premain(final String agentArgs, final Instrumentation inst) {
        globalInstrumentation = inst;
    }

    public static long getObjectSize(final Object object) {
        if (globalInstrumentation == null) {
            throw new IllegalStateException("Agent not initialized.");
        }
        return globalInstrumentation.getObjectSize(object);
    }
}
  • Avant de créer un fichier JAR pour cet agent, nous devons nous assurer qu’un métafichier simple, MANIFEST.MF , en fait partie **

Premain-class: com.baeldung.objectsize.InstrumentationAgent

Nous pouvons maintenant créer un fichier JAR d’agent avec le fichier MANIFEST.MF inclus. Une façon est via la ligne de commande:

javac InstrumentationAgent.java
jar cmf MANIFEST.MF InstrumentationAgent.jar InstrumentationAgent.class

3.2. Exemple de classe

Voyons cela en action en créant une classe avec des exemples d’objets qui utiliseront notre classe d’agent:

public class InstrumentationExample {

    public static void printObjectSize(Object object) {
        System.out.println("Object type: " + object.getClass() +
          ", size: " + InstrumentationAgent.getObjectSize(object) + " bytes");
    }

    public static void main(String[]arguments) {
        String emptyString = "";
        String string = "Estimating Object Size Using Instrumentation";
        String[]stringArray = { emptyString, string, "com.baeldung" };
        String[]anotherStringArray = new String[100];
        List<String> stringList = new ArrayList<>();
        StringBuilder stringBuilder = new StringBuilder(100);
        int maxIntPrimitive = Integer.MAX__VALUE;
        int minIntPrimitive = Integer.MIN__VALUE;
        Integer maxInteger = Integer.MAX__VALUE;
        Integer minInteger = Integer.MIN__VALUE;
        long zeroLong = 0L;
        double zeroDouble = 0.0;
        boolean falseBoolean = false;
        Object object = new Object();

        class EmptyClass {
        }
        EmptyClass emptyClass = new EmptyClass();

        class StringClass {
            public String s;
        }
        StringClass stringClass = new StringClass();

        printObjectSize(emptyString);
        printObjectSize(string);
        printObjectSize(stringArray);
        printObjectSize(anotherStringArray);
        printObjectSize(stringList);
        printObjectSize(stringBuilder);
        printObjectSize(maxIntPrimitive);
        printObjectSize(minIntPrimitive);
        printObjectSize(maxInteger);
        printObjectSize(minInteger);
        printObjectSize(zeroLong);
        printObjectSize(zeroDouble);
        printObjectSize(falseBoolean);
        printObjectSize(Day.TUESDAY);
        printObjectSize(object);
        printObjectSize(emptyClass);
        printObjectSize(stringClass);
    }

    public enum Day {
        MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
    }
}

Pour que cela fonctionne, nous devons inclure l’option – javaagent avec le chemin d’accès à l’agent JAR lors de l’exécution de notre application :

VM Options: -javaagent:"path__to__agent__directory\InstrumentationAgent.jar"

Le résultat de l’exécution de notre classe nous indiquera la taille estimée des objets:

Object type: class java.lang.String, size: 24 bytes
Object type: class java.lang.String, size: 24 bytes
Object type: class[Ljava.lang.String;, size: 32 bytes
Object type: class[Ljava.lang.String;, size: 416 bytes
Object type: class java.util.ArrayList, size: 24 bytes
Object type: class java.lang.StringBuilder, size: 24 bytes
Object type: class java.lang.Integer, size: 16 bytes
Object type: class java.lang.Integer, size: 16 bytes
Object type: class java.lang.Integer, size: 16 bytes
Object type: class java.lang.Integer, size: 16 bytes
Object type: class java.lang.Long, size: 24 bytes
Object type: class java.lang.Double, size: 24 bytes
Object type: class java.lang.Boolean, size: 16 bytes
Object type: class com.baeldung.objectsize.InstrumentationExample$Day, size: 24 bytes
Object type: class java.lang.Object, size: 16 bytes
Object type: class com.baeldung.objectsize.InstrumentationExample$1EmptyClass, size: 16 bytes
Object type: class com.baeldung.objectsize.InstrumentationExample$1StringClass, size: 16 bytes

4. Conclusion

Dans cet article, nous avons décrit comment la mémoire est utilisée par des types particuliers en Java, comment JVM stocke des données et souligne les éléments pouvant avoir une incidence sur la consommation totale de mémoire. Nous avons ensuite montré comment, dans la pratique, obtenir la taille estimée des objets Java.

Comme toujours, le code complet relatif à cet article se trouve dans le projet GitHub .