Ist Dalvik in Bezug auf die Objektgröße noch speicherhungriger als HotSpot?

Ich habe mich gefragt, wie viel Speicher ein Objekt auf Android belegt. Es gibt zahlreiche Ressourcen (wiediese) im Zusammenhang mit der HotSpot-JVM, die angibt, dass ein leeres Objekt 8 Byte und ein leeres Array 12 Byte benötigt und dass alle Objekte an der 8-Byte-Grenze ausgerichtet sind. Daher sollte ein Objekt ohne zusätzliche Felder 8 Bytes benötigen, das kleinste Objekt mit mindestens einem zusätzlichen Feld - 16 Bytes, ein leeres Array - 16 Bytes, oder?

Ich habe zu diesem Thema keine spezifischen Informationen über Dalvik gefunden und mich entschlossen, dies durch Tests herauszufinden. Ausführen des Tests hatteüberraschende Ergebnisse.

Ein paar Worte zur Berechnungsmethode. Die Android-Implementierung von Object.hashCode () gibt einfach den Zeiger auf das Objekt zurück, das in int umgewandelt wurde. (schien offensichtlich und allgemein, aber [eine weitere Überraschung], wie sich herausstellte, funktioniert dies beispielsweise NICHT bei HotSpot JVM - führen Sie MemTest mit HotSpot aus und sehen Sie nach). Also habe ich die Einfachheit von hashCode () auf Dalvik verwendet, um die Objektgröße auf Android zu berechnen, indem zwei Instanzen der getesteten Klasse hintereinander zugewiesen wurden und die Menge des zugewiesenen Speicherplatzes gleich der Differenz ihrer hashCode () sein sollte. Werte (unter der Annahme, dass es für Dalvik wenig sinnvoll ist, diese an völlig zufälligen Adressen zuzuweisen). Nur um sicherzugehen, dass ich immer 4 Objekte in einer Reihe pro Testklasse zugewiesen habe, was immer die gleiche Differenz von hashCode () ergab. Ich glaube also, dass es wenig Zweifel an der Richtigkeit der Methode gibt.

Hier ist der Quellcode des Tests:

public class MemTest {
    public static void run() {
        Object o1 = new Object();
        Object o2 = new Object();
        Object o3 = new Object();
        Object o4 = new Object();

        EmptyObject eo1 = new EmptyObject();
        EmptyObject eo2 = new EmptyObject();
        EmptyObject eo3 = new EmptyObject();
        EmptyObject eo4 = new EmptyObject();

        ObjectWithBoolean ob1 = new ObjectWithBoolean();
        ObjectWithBoolean ob2 = new ObjectWithBoolean();
        ObjectWithBoolean ob3 = new ObjectWithBoolean();
        ObjectWithBoolean ob4 = new ObjectWithBoolean();

        ObjectWithBooleanAndInt obi1 = new ObjectWithBooleanAndInt();
        ObjectWithBooleanAndInt obi2 = new ObjectWithBooleanAndInt();
        ObjectWithBooleanAndInt obi3 = new ObjectWithBooleanAndInt();
        ObjectWithBooleanAndInt obi4 = new ObjectWithBooleanAndInt();

        ObjectWithLong ol1 = new ObjectWithLong();
        ObjectWithLong ol2 = new ObjectWithLong();
        ObjectWithLong ol3 = new ObjectWithLong();
        ObjectWithLong ol4 = new ObjectWithLong();

        ObjectWith4Ints o4i1 = new ObjectWith4Ints();
        ObjectWith4Ints o4i2 = new ObjectWith4Ints();
        ObjectWith4Ints o4i3 = new ObjectWith4Ints();
        ObjectWith4Ints o4i4 = new ObjectWith4Ints();

        ObjectWith4IntsAndByte o4ib1 = new ObjectWith4IntsAndByte();
        ObjectWith4IntsAndByte o4ib2 = new ObjectWith4IntsAndByte();
        ObjectWith4IntsAndByte o4ib3 = new ObjectWith4IntsAndByte();
        ObjectWith4IntsAndByte o4ib4 = new ObjectWith4IntsAndByte();

        ObjectWith5Ints o5i1 = new ObjectWith5Ints();
        ObjectWith5Ints o5i2 = new ObjectWith5Ints();
        ObjectWith5Ints o5i3 = new ObjectWith5Ints();
        ObjectWith5Ints o5i4 = new ObjectWith5Ints();

        ObjectWithArrayRef oar1 = new ObjectWithArrayRef();
        ObjectWithArrayRef oar2 = new ObjectWithArrayRef();
        ObjectWithArrayRef oar3 = new ObjectWithArrayRef();
        ObjectWithArrayRef oar4 = new ObjectWithArrayRef();

        byte[] a0b1 = new byte[0];
        byte[] a0b2 = new byte[0];
        byte[] a0b3 = new byte[0];
        byte[] a0b4 = new byte[0];

        byte[] a1b1 = new byte[1];
        byte[] a1b2 = new byte[1];
        byte[] a1b3 = new byte[1];
        byte[] a1b4 = new byte[1];

        byte[] a5b1 = new byte[5];
        byte[] a5b2 = new byte[5];
        byte[] a5b3 = new byte[5];
        byte[] a5b4 = new byte[5];

        byte[] a9b1 = new byte[9];
        byte[] a9b2 = new byte[9];
        byte[] a9b3 = new byte[9];
        byte[] a9b4 = new byte[9];

        byte[] a12b1 = new byte[12];
        byte[] a12b2 = new byte[12];
        byte[] a12b3 = new byte[12];
        byte[] a12b4 = new byte[12];

        byte[] a13b1 = new byte[13];
        byte[] a13b2 = new byte[13];
        byte[] a13b3 = new byte[13];
        byte[] a13b4 = new byte[13];

        print("java.lang.Object", o1, o2, o3, o4);
        print("Empty object", eo1, eo2, eo3, eo4);
        print("Object with boolean", ob1, ob2, ob3, ob4);
        print("Object with boolean and int", obi1, obi2, obi3, obi4);
        print("Object with long", ol1, ol2, ol3, ol4);
        print("Object with 4 ints", o4i1, o4i2, o4i3, o4i4);
        print("Object with 4 ints and byte", o4ib1, o4ib2, o4ib3, o4ib4);
        print("Object with 5 ints", o5i1, o5i2, o5i3, o5i4);

        print("Object with array ref", new Object[]{oar1, oar2, oar3, oar4});

        print("new byte[0]", a0b1, a0b2, a0b3, a0b4);
        print("new byte[1]", a1b1, a1b2, a1b3, a1b4);
        print("new byte[5]", a5b1, a5b2, a5b3, a5b4);
        print("new byte[9]", a9b1, a9b2, a9b3, a9b4);
        print("new byte[12]", a12b1, a12b2, a12b3, a12b4);
        print("new byte[13]", a13b1, a13b2, a13b3, a13b4);
    }

    static void print(String title, Object... objects) {
        StringBuilder buf = new StringBuilder(title).append(":");
        int prevHash = objects[0].hashCode();
        int prevDiff = -1;
        for (int i = 1; i < objects.length; i++) {
            int hash = objects[i].hashCode();
            int diff = Math.abs(hash - prevHash);
            if (prevDiff == -1 || prevDiff != diff) {
                buf.append(' ').append(diff);
            }
            prevDiff = diff;
            prevHash = hash;
        }
        System.out.println(buf.toString());
    }

    /******** Test classes ******/

    public static class EmptyObject {
    }

    public static class ObjectWith4Ints {
        int i1;
        int i2;
        int i3;
        int i4;
    }

    public static class ObjectWith4IntsAndByte {
        int i1;
        int i2;
        int i3;
        int i4;
        byte b;
    }

    public static class ObjectWith5Ints {
        int i1;
        int i2;
        int i3;
        int i4;
        int i5;
    }

    public static class ObjectWithArrayRef {
        byte[] b;
    }

    public static class ObjectWithBoolean {
        boolean b;
    }

    public static class ObjectWithBooleanAndInt {
        boolean b;
        int i;
    }

    public static class ObjectWithLong {
        long l;
    }
}

und hier sind die Ergebnisse:

java.lang.Object: 16
Empty object: 16
Object with boolean: 16
Object with boolean and int: 24
Object with long: 24
Object with 4 ints: 32
Object with 4 ints and byte: 32
Object with 5 ints: 32
Object with array ref: 16
new byte[0]: 24
new byte[1]: 24
new byte[5]: 32
new byte[9]: 32
new byte[12]: 32
new byte[13]: 40

Um die Ergebnisse zusammenzufassen:

Die Ausrichtung der 8-Byte-Grenze ist die gleiche wie bei HotSpot, und das ist das einzige, was gleich ist.

Mindestens 16 Byte für ein einfaches Objekt (gegenüber 8 bei HotSpot)

Anscheinend belegt ein leeres Objekt selbst 12 Bytes (gegenüber 8 bei HotSpot) und es ist Platz für 4 zusätzliche Bytes, bis die Objektgröße von 16 Bytes zur nächsten Grenze von 24 Bytes "springt".

Mindestens 24 Byte für ein leeres Array (gegenüber 12 bei HotSpot)

In ähnlicher Weise belegt ein Array selbst 20 Bytes (gegenüber 12 bei HotSpot) und es ist Platz für 4 zusätzliche Bytes an Array-Daten, bis die Objektgröße von 24 Bytes zur nächsten Grenze von 32 Bytes "springt".

ZUSATZ: (In Reaktion auf Louis 'Vorschlag) Ein weiterer Stresstest zeigt, dass selbst bei der Zuweisung von einer Million von Objektinstanzen der Abstand zwischen zwei Objekten NIEMALS weniger als 16 Byte beträgt. Dies ist der Beweis, dass potenzielle 8-Byte-Löcher zwischen den Objekten definitiv toter Raum für weitere Zuweisungen sind. Andernfalls hätte Dalvik bis zu dem Zeitpunkt, an dem ungefähr die Hälfte des Speichers für Objekte reserviert wurde, definitiv auch einige von ihnen in "Löcher" stecken sollen. und der Stresstest würde 8 ergeben, nicht 16.

public static void run2() {
    int count = 1024 * 1024;
    Object[] arr = new Object[count];
    for (int i = 0; i < count; i++) {
        arr[i] = new Object();
    }
    int[] hashes = new int[count];
    for (int i = 0; i < count; i++) {
        hashes[i] = arr[i].hashCode();
    }
    Arrays.sort(hashes);

    int minDist = Integer.MAX_VALUE;
    for (int i = 1; i < count; i++) {
        int dist = Math.abs(hashes[i] - hashes[i - 1]);
        if (dist < minDist) {
            minDist = dist;
        }
    }
    System.out.println("Allocated "+ count + " Objects, minimum distance is "+ minDist);
}

Sehe ich es richtig, dass DalviksObjekt benötigt bis zu 8 weitere Bytes undArray 8-12 weitere Bytes im Vergleich zu HotSpot?

Antworten auf die Frage(2)

Ihre Antwort auf die Frage