A Dalvik tem ainda mais fome de memória que o HotSpot em termos de tamanho de objeto?
Eu tenho me perguntado quanto de memória um objeto ocupa no Android. Existem inúmeros recursos (comoisto) relacionado à HotSpot JVM informando que um objeto vazio ocupa 8 bytes e um array vazio de 12 bytes e que todos os objetos estão alinhados ao limite de 8 bytes. Assim, um objeto sem campos extras deve ter 8 bytes, o menor objeto com pelo menos um campo extra - 16 bytes, uma matriz vazia - 16 bytes, certo?
Não encontrei nenhuma informação específica sobre a Dalvik sobre este assunto e decidi descobrir através de testes. A execução do teste teveresultados surpreendentes.
Poucas palavras sobre o método de cálculo. Implementação do Android de Object.hashCode () simplesmente retorna o ponteiro para o objeto fundido para int. (Parecia óbvio e geral, mas [outra surpresa], como aconteceu, ele não usa a JVM do HotSpot, por exemplo - execute o MemTest com o HotSpot e veja). Então, eu usei a simplicidade de hashCode () no Dalvik para calcular o tamanho do objeto no Android, alocando duas instâncias da classe testada em uma linha e a quantidade do espaço alocado deve ser igual à diferença do seu hashCode () valores (assumindo que faz pouco sentido para a Dalvik alocar aqueles em endereços completamente aleatórios). Só para ter certeza eu aloquei sempre 4 objetos em uma linha por classe de teste, que entregam sempre a mesma diferença de hashCode (). Então, acredito que há pouca dúvida sobre a exatidão do método.
Aqui está o código fonte do teste:
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;
}
}
e aqui estão os 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 os resultados:
O alinhamento de limite de 8 bytes é o mesmo que no HotSpot, e essa é a única coisa que é a mesma.
mínimo de 16 bytes para um objeto simples (vs 8 no HotSpot)
aparentemente, um objeto vazio em si ocupa 12 bytes (contra 8 no HotSpot) e há espaço para 4 bytes extras até que o tamanho do objeto "salte" de 16 bytes para o próximo limite de 24 bytes.
mínimo de 24 bytes para uma matriz vazia (vs 12 no HotSpot)
da mesma forma, um array ocupa 20 bytes (vs 12 no HotSpot) e há espaço para 4 bytes extras de dados de array até que o tamanho do objeto "salte" de 24 bytes para o próximo limite de 32 bytes.
ADIÇÃO: (em resposta à sugestão de Louis) Outro teste de estresse mostra que, mesmo alocando um milhão de instâncias de objetos, a distância entre quaisquer dois nunca é inferior a 16 bytes. Essa é a prova de que potenciais buracos de 8 bytes entre os objetos são definitivamente espaço morto para alocações adicionais, caso contrário, quando metade da memória for alocada para Objetos, a dalvik definitivamente deveria ter colocado alguns deles em 'buracos' também, e o teste de estresse retornaria 8, não 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);
}
Eu vejo bem que o DalvikObjeto leva até mais 8 bytes ematriz 8-12 mais bytes comparado ao HotSpot?