¿Dalvik tiene más memoria que HotSpot en términos de tamaños de objetos?

Me he estado preguntando cuánta memoria ocupa un objeto en Android. Hay numerosos recursos (comoesta) relacionado con la JVM de HotSpot que indica que un objeto vacío toma 8 bytes y una matriz vacía de 12 bytes y que todos los objetos están alineados con el límite de 8 bytes. Por lo tanto, un objeto sin campos adicionales debe tomar 8 bytes, el objeto más pequeño con al menos un campo adicional - 16 bytes, una matriz vacía - 16 bytes, ¿verdad?

No he encontrado información específica sobre Dalvik sobre este asunto y decidí averiguarlo mediante pruebas. Corriendo la prueba teniaresultados sorprendentes.

Pocas palabras sobre el método de cálculo. La implementación de Object.hashCode () en Android simplemente devuelve el puntero al objeto convertido en int. (Parecía obvio y general, pero [otra sorpresa], como resultó, NO en HotSpot JVM, por ejemplo, ejecute MemTest con HotSpot y vea). Por lo tanto, he usado la simplicidad de hashCode () en Dalvik para calcular el tamaño del objeto en Android asignando dos instancias de la clase probada en una fila y la cantidad de espacio asignado debe ser igual a la diferencia de su hashCode () valores (suponiendo que tiene poco sentido que Dalvik los asigne en direcciones completamente aleatorias). Solo para estar seguro de que siempre he asignado 4 objetos en una fila por clase de prueba, que entregó siempre la misma diferencia de hashCode (). Entonces, creo que hay pocas dudas sobre la corrección del método.

Aquí está el código fuente de la prueba:

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

Y aquí están los resultados:

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

Para resumir los resultados:

La alineación del límite de 8 bytes es la misma que en HotSpot, y eso es lo único que es lo mismo.

Mínimo de 16 bytes para un objeto simple (vs 8 en HotSpot)

al parecer, un objeto vacío ocupa 12 bytes (vs 8 en HotSpot) y hay espacio para 4 bytes adicionales hasta que el tamaño del objeto "salta" de 16 bytes al siguiente límite de 24 bytes.

Mínimo de 24 bytes para una matriz vacía (vs 12 en HotSpot)

de manera similar, una matriz en sí misma ocupa 20 bytes (frente a 12 en HotSpot) y hay espacio para 4 bytes adicionales de datos de la matriz hasta que el tamaño del objeto 'salta' de 24 bytes al siguiente límite de 32 bytes.

ADICIÓN: (en respuesta a la sugerencia de Louis) Otra prueba de esfuerzo muestra que, incluso asignando un millón de instancias de Objeto, la distancia entre cualquiera de los dos NUNCA es inferior a 16 bytes. Esa es la prueba de que los orificios potenciales de 8 bytes entre los objetos son definitivamente un espacio muerto para futuras asignaciones, de lo contrario, cuando ya se haya asignado la mitad de la memoria para los Objetos, dalvik definitivamente debería haber puesto algunos de ellos en "orificios", y la prueba de estrés volvería 8, no 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);
}

¿Veo bien que Dalvik?El objeto toma hasta 8 bytes más. ymatriz 8-12 bytes más en comparación con HotSpot?

Respuestas a la pregunta(2)

Su respuesta a la pregunta