wake-up-neo.net

Größe eines Bytes im Speicher - Java

Ich habe gemischte Meinungen über die Menge an Speicher gehört, die ein Byte in einem Java-Programm beansprucht.

Es ist mir bekannt, dass Sie nicht mehr als +127 in einem Java-Byte speichern können. Die Dokumentation besagt, dass ein Byte nur 8 Bit umfasst, aber hier mir wird gesagt, dass es tatsächlich genauso viel Speicher benötigt als int, und ist daher nur ein Typ, der zum Verständnis des Codes und nicht zur Effizienz beiträgt. 

Kann jemand das klären, und wäre dies ein implementierungsspezifisches Problem?

48
Ben Page

Okay, es wurde viel diskutiert und nicht viel Code :)

Hier ist ein kurzer Benchmark. Es gibt die normalen Vorbehalte, wenn es um diese Art von Dingen geht - das Testen von Gedächtnissen hat aufgrund von JITting usw. Ungewöhnliches, aber bei entsprechend großen Zahlen ist es trotzdem nützlich. Es gibt zwei Typen mit jeweils 80 Mitgliedern - LotsOfBytes hat 80 Byte, LotsOfInts 80 Ints. Wir bauen viele davon, stellen sicher, dass sie nicht GC-geprüft sind, und überprüfen die Speicherbelegung:

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();
    }
}

Ausgabe auf meiner Box:

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

Offensichtlich gibt es also einen gewissen Overhead - 8 Bytes, so wie es aussieht, obwohl irgendwie nur 7 für LotsOfInts sind (wie ich sagte, es gibt hier Kuriositäten) - aber der Punkt ist, dass die Byte-Felder für LotsOfBytes so eingepackt zu sein scheinen nimmt (nach dem Entfernen des Overheads) nur ein Viertel so viel Speicher wie LotsOfInts.

59
Jon Skeet

Ja, eine Byte-Variable ist tatsächlich 4 Byte im Speicher. Dies gilt jedoch nicht für Arrays. Ein Bytearray von 20 Bytes ist tatsächlich nur 20 Bytes im Speicher. Das liegt daran, dass die Java-Bytecode-Sprache nur Ints und Longs als Zahlentypen kennt (sie muss also alle Zahlen als beide Arten behandeln, 4 Bytes oder 8 Bytes), jedoch Arrays mit jeder möglichen Nummerngröße (also kurze Arrays) Fakt ist, dass zwei Bytes pro Eintrag und Byte-Arrays tatsächlich ein Byte pro Eintrag sind.

17
Mecki

Java ist niemals implementierungs- oder plattformspezifisch (zumindest bei primitiven Schriftgrößen ). Ihre primitiven Typen bleiben immer gleich, egal auf welcher Plattform Sie sich befinden. Dies unterscheidet sich von C und C++ (und wurde als Verbesserung angesehen), wo einige der primitiven Typen plattformspezifisch waren.

Da es für das zugrunde liegende Betriebssystem schneller ist, vier (oder acht, in einem 64-Bit-System) Bytes gleichzeitig zu adressieren, kann die JVM möglicherweise mehr Bytes zum Speichern eines primitiven Bytes zuordnen. Sie können jedoch nur Werte von -128 bis speichern 127 drin.

7
Bill the Lizard

Ich habe einen Test mit http://code.google.com/p/memory-measurer/ .__ durchgeführt. Beachten Sie, dass ich 64-Bit Oracle/Sun Java 6 ohne Komprimierung von Referenzen usw. verwende.

Jedes Objekt belegt etwas Platz, und JVM muss die Adresse dieses Objekts kennen, und "Adresse" selbst ist 8 Byte groß.

Bei Primitiven wird das Aussehen von Primitiven für eine bessere Leistung (natürlich!) Auf 64 Bit umgewandelt:

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

Mit Arrays:

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

Und jetzt ratet mal was ...

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

P.S. Oracle Java 6, neueste und beste Version, 64-Bit, 1.6.0_37, MacOS X

5
Fuad Efendi

Eine aufschlussreiche Übung besteht darin, javap auf einem Code auszuführen, der einfache Dinge mit Bytes und Ints erledigt. Sie sehen Bytecodes, die erwarten, dass int-Parameter für Bytes arbeiten, und Bytecodes, die eingefügt werden, um von einem zum anderen zu gelangen.

Beachten Sie, dass Byte-Arrays jedoch nicht als Arrays mit 4-Byte-Werten gespeichert werden. Ein Byte mit einer Länge von 1024 Byte belegt daher 1 KB Speicher (Ignorieren von Overheads).

5
izb

Es hängt davon ab, wie die JVM Padding anwendet usw. Ein Array von Bytes wird (in einem vernünftigen System) in 1-Byte-Element gepackt, aber eine Klasse mit vier Byte-Feldern kann entweder eng gepackt oder an Word-Grenzen aufgefüllt werden Es ist abhängig von der Implementierung.

3
Jon Skeet

Was Ihnen gesagt wurde, ist genau richtig. Die Java-Bytecode-Spezifikation hat nur 4-Byte-Typen und 8-Byte-Typen. 

byte, Char, Int, Short, Boolean und Float werden jeweils in 4 Byte gespeichert.

double und long werden in 8 Bytes gespeichert.

Byte-Code ist jedoch nur die halbe Wahrheit. Es gibt auch die JVM, die implementierungsspezifisch ist. Es gibt genug Informationen im Java-Bytecode, um festzustellen, dass eine Variable als Byte deklariert wurde. Ein JVM-Implementierer kann entscheiden, nur ein Byte zu verwenden, obwohl das meiner Meinung nach höchst unwahrscheinlich ist.

2
Steve McLeod

Sie können immer longs verwenden und die Daten in sich selbst packen, um die Effizienz zu steigern. Dann können Sie immer darauf achten, dass Sie alle 4 Bytes verwenden.

byte = 8bit = ein Byte, das in der Java-Spezifikation definiert ist. 

wie viel Speicher ein Bytearray benötigt, ist nicht durch die Spezifikation definiert, und es ist nicht definiert, wie viel ein komplexes Objekt benötigt. 

Für die Sun JVM habe ich die Regeln dokumentiert: https://www.sdn.sap.com/irj/sdn/weblogs?blog=/pub/wlg/5163

2
kohlerm

Ich wollte nur darauf hinweisen, dass die Aussage

du kannst nicht mehr als +127 in einem Java-Byte speichern

ist nicht wirklich richtig.

Sie können immer 256 verschiedene Werte in einem Byte speichern. Daher können Sie den 0..255-Bereich wie einen "vorzeichenlosen" Byte verwenden.

Es hängt alles davon ab, wie Sie mit diesen 8 Bits umgehen.

Beispiel:

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

Siehe meine MonitoringTools auf meiner Website (www.csd.uoc.gr/~andreou)

 Klasse X {
 Byte b1, b2, b3 ...; 
} 

 long memoryUsed = MemoryMeasurer.measure (neues X ()); 

(Kann auch für komplexere Objekte/Objektdiagramme verwendet werden)

In Suns 1.6 JDK scheint es, dass ein Byte tatsächlich ein einzelnes Byte benötigt (in älteren Versionen int ~ Byte in Bezug auf den Arbeitsspeicher). Beachten Sie jedoch, dass Byte [] auch in älteren Versionen auf ein Byte pro Eintrag gepackt wurde.

Der Punkt ist auf jeden Fall, dass es nicht nötig ist, komplexe Tests wie die von Jon Skeet oben durchzuführen, die lediglich Schätzungen abgeben. Wir können die Größe eines Objekts direkt messen!

0

Beim Lesen der obigen Kommentare scheint es, dass meine Schlussfolgerung für viele eine Überraschung sein wird (dies ist auch eine Überraschung für mich), und es lohnt sich zu wiederholen:

  • Die alte Größe (int) == Größe (Byte) für Variablen enthält nicht mehrzumindest in Suns Java 6.

Stattdessen Größe (Byte) == 1 Byte (!!)

0