Join FlipAndroid.COM Telegram Group: https://t.me/joinchat/F_aqThGkhwcLzmI49vKAiw


¿Es Dalvik aún más hambriento de memoria que HotSpot en términos de tamaños de objeto?

Me pregunto cuánta memoria ocupa un objeto en Android. Existen numerosos recursos (como este ) relacionados con la JVM de HotSpot que dice 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 un límite de 8 bytes. Por lo tanto, un objeto sin campos adicionales debe tener 8 bytes, el objeto más pequeño con al menos un campo extra – 16 bytes, una matriz vacía – 16 bytes, ¿verdad?

No he encontrado ninguna información específica sobre Dalvik sobre este asunto y decidí averiguarlo mediante pruebas. Ejecutar la prueba tuvo resultados sorprendentes .

  • No hay lista de dispositivos en Android DDMS
  • Android - cómo anular el registro de un receptor creado en el manifiesto?
  • Android Rotar imagen antes de guardar
  • ¿Podemos encontrar elemento por ID en appium
  • ¿Visualización de archivos de activos de Android en un WebView?
  • Crear PhoneGap 3.0.0 error de proyecto - 'plataforma add android' no es un nodo
  • Pocas palabras sobre el método de cálculo. La implementación de Object.hashCode () de Android simplemente devuelve el puntero al objeto lanzado a int. (Parecía obvio y general, pero [otra sorpresa] como resultó, no es en HotSpot JVM por ejemplo – ejecutar MemTest con HotSpot y ver). Por lo tanto, he utilizado la simplicidad de hashCode () en Dalvik para calcular el tamaño del objeto en Android mediante la asignación de 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 para Dalvik para asignar aquellos en direcciones completamente aleatorio). Sólo para estar seguro de haber asignado siempre 4 objetos en una fila por clase de prueba, que entregó siempre la misma diferencia de hashCode (). Por lo tanto, creo que hay poca duda 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 esa es la única cosa que es la misma.

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

    • Aparentemente 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 ocupa 20 bytes (vs 12 en HotSpot) y hay espacio para 4 bytes adicionales de datos de matriz hasta que el tamaño del objeto 'salte' de 24 bytes al siguiente límite de 32 bytes.

    ADICIÓN: (en respuesta a la sugerencia de Louis) Otra prueba de estrés muestra que incluso asignar un millón de instancias de objeto la distancia entre dos cualquiera es NUNCA menor de 16 bytes. Esa es la prueba de que los posibles orificios de 8 bytes entre los objetos son definitivamente espacio muerto para asignaciones adicionales, de lo contrario en el momento en que aproximadamente la mitad de la memoria se ha asignado a objetos dalvik definitivamente debería haber estado poniendo algunos de ellos en "agujeros" 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); } 

    ¿Lo veo bien que el objeto de Dalvik toma hasta 8 bytes más y la matriz 8-12 bytes más en comparación con HotSpot?

  • "Error al resolver: com.android.support:support-v4:26.0.0" y otros errores similares en la sincronización de Gradle
  • Enlace de datos de Android con un adaptador personalizado
  • Animar actividad que no es parte de tu aplicación
  • Aplicación de Android no en Market: cómo enviar actualizaciones?
  • SearchView Expandir / contraer la animación en Android
  • ¿Cómo terminar Actividad al iniciar otra actividad en Android?
  • 2 Solutions collect form web for “¿Es Dalvik aún más hambriento de memoria que HotSpot en términos de tamaños de objeto?”

    (Sí, esta es una vieja pregunta, pero los resultados fueron un poco interesantes así que lo empujé un poco.)

    El método Object.clone() necesita hacer una copia completa a bit de un objeto. Para ello, necesita saber qué tan grande es un objeto. Si observa dvmCloneObject() , verá que utiliza un método para arrays y un método diferente para objetos.

    Para arrays, llama dvmArrayObjectSize() , que multiplica la longitud de la matriz por el ancho del elemento (1, 2, 4 u 8) y, a continuación, agrega el desplazamiento de los datos de la matriz desde el inicio del objeto. Cada objeto tiene un encabezado de 8 bytes; Los arrays tienen un ancho de 4 bytes e incluyen 4 bytes adicionales de relleno para asegurar que los valores de 64 bits estén alineados correctamente. Así que para una matriz de 5 elementos de short , sería de 16 + 5 * 2.

    Para objetos ordinarios, sólo utiliza el campo objectSize en el objeto de clase. Esto se establece por una función bastante complicada llamada computeFieldOffsets() . Esta función asegura que todas las referencias de objetos vienen primero (por lo que el GC puede saltar menos al escanear), a continuación, sigue con todos los campos de 64 bits. Para asegurarse de que los campos de 64 bits están correctamente alineados, puede mover uno de los campos primitivos de 32 bits hasta las cosas de la almohadilla. (Si no hay ningún campo de 32 bits apropiado, sólo obtendrá 4 bytes de relleno.)

    Debo añadir: todos los campos son de 32 bits, excepto long y double , que son de 64 bits. Las referencias de objetos son de 32 bits.

    Por lo tanto, es difícil decir exactamente qué tan grande será un objeto que no sea de matriz, pero en general se toma el encabezado de 8 bytes, se suman los anchos de los campos adicionales y se redondea al siguiente múltiplo de 8 bytes. Último porque todos los objetos deben estar alineados con 64 bits.

    Así que esa es la teoría. Para verlo en la práctica, agregué esto a dvmCloneObject() :

     ALOGD("class=%s size=%d", clazz->descriptor, clazz->objectSize); 

    Y vio salida logcat como:

     D dalvikvm: class=Ljava/util/Locale; size=24 D dalvikvm: class=Ljava/util/Date; size=16 

    La configuración regional tiene 4 campos de referencia, la fecha tiene un campo long , por lo que estos valores coinciden con las expectativas.

    Idealmente, eso es exactamente cuánto espacio sería requerido. Sin embargo, el objeto se asigna con mspace_calloc() , que añade otros 4 o (a veces) 8 bytes de sobrecarga. Así que el espacio real requerido para los valores anteriores sería 32 y 24, que coincide con sus resultados experimentales.

    No tengo las respuestas para usted, pero puedo sugerir un par de lugares que podría buscar en la fuente para obtener más información.

    Puede echar un vistazo a las estructuras DataObject y ArrayObject en dalvik / vm / oo / Object.h. Sobre la base de esto, parece que un objeto vacío sólo debe tomar 8 bytes, mientras que una matriz vacía debe tomar 12 bytes. Esto no parece coincidir con sus resultados, aunque no estoy seguro de por qué.

    También puede ver los usos del campo objectSize en la estructura ClassObject para obtener más información. Una búsqueda rápida de los usos de ese campo muestra que el método dvmAllocObject en dalvik / vm / alloc / Alloc.cpp parece ser responsable de asignar la memoria para nuevos objetos.

    FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.