web-dev-qa-db-fra.com

Taille d'un octet en mémoire - Java

J'ai entendu des opinions mitigées sur la quantité de mémoire qu'un octet occupe dans un programme Java.

Je suis conscient que vous ne pouvez pas stocker plus de +127 caractères dans un octet Java, et la documentation indique qu'un octet ne fait que 8 bits, mais ici on me dit qu'il utilise en réalité autant de mémoire en tant qu'int, et n'est donc qu'un type qui aide à la compréhension du code et non à l'efficacité. 

Quelqu'un peut-il éclaircir cette question et s'agirait-il d'un problème spécifique à la mise en œuvre?

48
Ben Page

D'accord, il y a eu beaucoup de discussions et pas beaucoup de code :)

Voici un repère rapide. Il y a des mises en garde normales à propos de ce genre de choses - tester la mémoire a des bizarreries dues au JIT, etc., mais avec de grands nombres, c'est utile quand même. Il a deux types, chacun avec 80 membres - LotsOfBytes a 80 octets, LotsOfInts a 80 ints. Nous en construisons beaucoup, assurez-vous qu'ils ne sont pas corrigés et vérifiez l'utilisation de la mémoire:

class LotsOfBytes
{
    byte a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af;
    byte b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf;
    byte c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, ca, cb, cc, cd, ce, cf;
    byte d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, da, db, dc, dd, de, df;
    byte e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, ea, eb, ec, ed, ee, ef;
}

class LotsOfInts
{
    int a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af;
    int b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf;
    int c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, ca, cb, cc, cd, ce, cf;
    int d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, da, db, dc, dd, de, df;
    int e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, ea, eb, ec, ed, ee, ef;
}


public class Test
{
    private static final int SIZE = 1000000;

    public static void main(String[] args) throws Exception
    {        
        LotsOfBytes[] first = new LotsOfBytes[SIZE];
        LotsOfInts[] second = new LotsOfInts[SIZE];

        System.gc();
        long startMem = getMemory();

        for (int i=0; i < SIZE; i++)
        {
            first[i] = new LotsOfBytes();
        }

        System.gc();
        long endMem = getMemory();

        System.out.println ("Size for LotsOfBytes: " + (endMem-startMem));
        System.out.println ("Average size: " + ((endMem-startMem) / ((double)SIZE)));

        System.gc();
        startMem = getMemory();
        for (int i=0; i < SIZE; i++)
        {
            second[i] = new LotsOfInts();
        }
        System.gc();
        endMem = getMemory();

        System.out.println ("Size for LotsOfInts: " + (endMem-startMem));
        System.out.println ("Average size: " + ((endMem-startMem) / ((double)SIZE)));

        // Make sure nothing gets collected
        long total = 0;
        for (int i=0; i < SIZE; i++)
        {
            total += first[i].a0 + second[i].a0;
        }
        System.out.println(total);
    }

    private static long getMemory()
    {
        Runtime runtime = Runtime.getRuntime();
        return runtime.totalMemory() - runtime.freeMemory();
    }
}

Sortie sur ma boite:

Size for LotsOfBytes: 88811688
Average size: 88.811688
Size for LotsOfInts: 327076360
Average size: 327.07636
0

Donc, évidemment, il y a quelques frais généraux - 8 octets d'après l'apparence de celui-ci, bien que de manière générale, seuls 7 pour LotsOfInts (- comme je l'ai dit, il y a des bizarreries ici) - mais le fait est que les champs d'octet semblent être emballés dans LotsOfBytes de telle sorte qu'il prend (après suppression des frais généraux) seulement un quart de la mémoire de LotsOfInts.

59
Jon Skeet

Oui, une variable d'octet est en fait de 4 octets en mémoire. Cependant, cela ne vaut pas pour les tableaux. Un tableau d'octets de 20 octets ne représente en réalité que 20 octets en mémoire. En effet, le langage Java Bytecode Language ne connaît que les entrées et les longues comme types de nombres (il doit donc gérer tous les nombres comme types, soit 4 octets ou 8 octets), mais il connaît les tableaux de toutes les tailles possibles fait, deux octets par entrée et les tableaux d’octets sont en fait d’un octet par entrée).

17
Mecki

Java n'est jamais spécifique à une implémentation ou à une plate-forme (du moins en ce qui concerne les tailles de type primitives sont concernées). Les types primitifs ont toujours la garantie de rester les mêmes quelle que soit la plate-forme sur laquelle vous vous trouvez. Cela diffère de (et a été considéré comme une amélioration sur) C et C++, où certains des types primitifs étaient spécifiques à la plate-forme.

Étant donné que le système d'exploitation sous-jacent gère plus rapidement quatre octets (ou huit dans un système 64 bits) à la fois, la machine virtuelle Java peut allouer plus d'octets pour stocker un octet primitif, mais vous ne pouvez toujours stocker que les valeurs de -128 à 127 dedans.

7
Bill the Lizard

J'ai fait un test avec http://code.google.com/p/memory-measurer/ Notez que j'utilise Oracle/Sun Java 6 64 bits, sans compression des références, etc.

Chaque objet occupe un espace, plus JVM doit connaître l'adresse de cet objet, et "adresse" lui-même est de 8 octets.

Avec les primitives, on dirait que les primitives sont converties en 64 bits pour de meilleures performances (bien sûr!):

byte: 16 bytes,
 int: 16 bytes,
long: 24 bytes.

Avec des tableaux:

byte[1]: 24 bytes
 int[1]: 24 bytes
long[1]: 24 bytes

byte[2]: 24 bytes
 int[2]: 24 bytes
long[2]: 32 bytes

byte[4]: 24 bytes
 int[4]: 32 bytes
long[4]: 48 bytes

byte[8]: 24 bytes => 8 bytes, "start" address, "end" address => 8 + 8 + 8 bytes
 int[8]: 48 bytes => 8 integers (4 bytes each), "start" address, "end" address => 8*4 + 8 + 8 bytes
long[8]: 80 bytes => 8 longs (8 bytes each), "start" address, "end" address => 8x8 + 8 + 8 bytes

Et maintenant devinez quoi ...

    byte[8]: 24 bytes
 byte[1][8]: 48 bytes
   byte[64]: 80 bytes
 byte[8][8]: 240 bytes

P.S. Oracle Java 6, dernier et dernier logiciel, 64 bits, 1.6.0_37, MacOS X

5
Fuad Efendi

Un exercice révélateur consiste à exécuter javap sur un code faisant des choses simples avec des octets et des entiers. Vous verrez des bytecodes qui attendent des paramètres int fonctionnant sur des octets, et des bytecodes insérés pour co-erceer de l'un à l'autre.

Notez cependant que les tableaux d'octets ne sont pas stockés en tant que tableaux de valeurs sur 4 octets. Un tableau d'octets de 1024 mètres de long consomme 1k de mémoire (en ignorant tout temps système).

5
izb

Cela dépend de la façon dont la machine virtuelle Java applique le remplissage, etc. Un tableau d'octets (dans n'importe quel système sain) est compressé dans un octet par élément, mais une classe avec des champs de quatre octets peut être compactée ou complétée dans des limites de mots - cela dépend de la mise en œuvre.

3
Jon Skeet

Ce que vous avez dit est tout à fait exact. La spécification de code d'octet Java ne comporte que des types de 4 octets et des types de 8 octets. 

byte, char, int, short, boolean, float sont tous stockés dans 4 octets chacun.

double et long sont stockés dans 8 octets.

Cependant, le code d'octet n'est que la moitié de l'histoire. Il y a aussi la JVM, qui est spécifique à l'implémentation. Le code d'octet Java contient suffisamment d'informations pour déterminer qu'une variable a été déclarée sous forme d'octet. Un réalisateur JVM peut décide de n'utiliser qu'un octet, bien que cela soit hautement improbable.

2
Steve McLeod

Vous pouvez toujours utiliser des longs et emballer les données en vous-même pour augmenter l'efficacité. Ensuite, vous pouvez toujours vous assurer que vous utiliserez les 4 octets.

2

octet = 8 bits = un octet défini par la spécification Java. 

la quantité de mémoire nécessaire à un tableau d'octets est not définie par la spécification, ni n'est définie la quantité de besoins d'un objet complexe. 

Pour Sun JVM, j’ai documenté les règles: https://www.sdn.sap.com/irj/sdn/weblogs?blog=/pub/wlg/5163

2
kohlerm

Je voulais juste souligner que la déclaration

vous ne pouvez pas stocker plus de +127 dans un octet Java

n'est pas vraiment correct.

Vous pouvez toujours stocker 256 valeurs différentes dans un octet. Par conséquent, vous pouvez facilement avoir votre plage 0..255 comme s'il s'agissait d'un octet "non signé".

Tout dépend de la façon dont vous gérez ces 8 bits.

Exemple:

byte B=(byte)200;//B contains 200
System.out.println((B+256)%256);//Prints 200
System.out.println(B&0xFF);//Prints 200
0
Unai Vivi

Voir mes outils de surveillance sur mon site (www.csd.uoc.gr/~andreou)

 classe X {
 octet b1, b2, b3 ...; 
} 

 longue mémoire utilisée = MemoryMeasurer.measure (new X ()); 

(Il peut également être utilisé pour des objets/graphiques d'objets plus complexes)

Dans le JDK 1.6 de Sun, il semble qu’un octet prenne bien un octet (dans les versions antérieures, entre octets en mémoire). Cependant, notez que même dans les versions antérieures, byte [] était également compressé à un octet par entrée.

Quoi qu’il en soit, le fait est qu’il n’est pas nécessaire de recourir à des tests complexes comme celui de Jon Skeet ci-dessus, qui ne donnent que des estimations. Nous pouvons mesurer directement la taille d'un objet!

0
Dimitris Andreou

En lisant les commentaires ci-dessus, il semble que ma conclusion sera une surprise pour beaucoup (c'est aussi une surprise pour moi), il convient donc de le répéter:

  • L'ancienne taille (int) == taille (octet) pour les variables ne tient plus, du moins dans Java 6 de Sun.

Au lieu de cela, taille (octet) == 1 octet (!!)

0
Dimitris Andreou