Android de adentro hacia afuera: una comparación de Dalvik y ART

¡Hola, Habr! Hace aproximadamente medio año publiqué una "guía" detallada de JVM . La publicación, en general, fue, y en los comentarios preguntaron si "hay algo planeado para Android". Finalmente, lo puse en mis manos.







En esta publicación, hablaremos sobre el tiempo de ejecución de Android. En particular, intentaré describir breve pero sucintamente en qué se diferencian ART y Dalvik, y cómo las herramientas de desarrollo de Android han mejorado con el tiempo. El tema claramente no es nuevo, pero espero que sea útil para aquellos que recién están comenzando a profundizar en él. A quién le importa, bienvenido a cat.



Máquina virtual



Primero, veamos en qué se diferencia la JVM del DVM.



Java Virtual Machine es una máquina virtual capaz de ejecutar código de bytes de Java independientemente de la plataforma subyacente. Se basa en el principio de "escribir una vez, ejecutar en cualquier lugar". El código de bytes de Java se puede ejecutar en cualquier máquina que admita JVM.



El compilador de Java convierte archivos .java en archivos de clase (código de bytes). El código de bytes se pasa a la JVM, que lo compila en código de máquina para su ejecución directamente en la CPU.



Características de JVM:



  • Tiene una arquitectura de pila: una pila se utiliza como estructura de datos donde se colocan y almacenan los métodos. Funciona en el esquema LIFO o "Último en entrar - Primero en salir" o "Último en entrar, primero en salir".
  • Solo puede ejecutar archivos de clase.
  • Utiliza un compilador JIT.


La máquina virtual Dalvik (DVM) es una máquina virtual Java desarrollada y escrita por Dan Bornstein y otros como parte de la plataforma móvil Android.



Podemos decir que Dalvik es el entorno para ejecutar componentes del sistema operativo Android y aplicaciones personalizadas. Cada proceso se ejecuta en su propio espacio de direcciones aislado. Cuando el usuario inicia la aplicación (o el sistema operativo inicia uno de sus componentes), el núcleo de la máquina virtual Dalvik (Zygote Dalvik VM) crea un proceso protegido separado en la memoria compartida, en el que la VM se implementa directamente como un entorno para iniciar la aplicación. En otras palabras, desde el interior, Android parece un conjunto de máquinas virtuales Dalvik, cada una de las cuales ejecuta una aplicación.



Características de DVM:



  • Utiliza una arquitectura basada en registros: la estructura de datos donde se colocan los métodos se basa en los registros del procesador. Debido a la ausencia de operaciones POP y PUSH, las instrucciones en una máquina virtual registrada se ejecutan más rápido que instrucciones similares en una máquina virtual apilada.
  • Ejecuta el código de bytes de formato nativo: Android dexer (hablaremos de ello a continuación) convierte los archivos de clase a formato .dex, optimizados para su ejecución en Dalvik VM. A diferencia de un archivo de clase, un archivo dex contiene varias clases a la vez.








Puede leer más sobre la arquitectura DVM aquí .



Android Dexer



Los desarrolladores de Android saben que el proceso de conversión de código de bytes de Java a código de bytes .dex para Android Runtime es un paso clave en la creación de un APK. El compilador dex trabaja principalmente bajo el capó en el desarrollo diario de aplicaciones, pero afecta directamente el tiempo de compilación de la aplicación, el tamaño del archivo .dex y el rendimiento en tiempo de ejecución.



Como ya se mencionó, el archivo dex en sí contiene varias clases a la vez. Las líneas duplicadas y otras constantes utilizadas en varios archivos de clase se incluyen solo para ahorrar espacio. El código de bytes de Java también se convierte en un conjunto de instrucciones alternativo utilizado por DVM. El archivo dex sin comprimir suele ser un poco más pequeño que el archivo Java comprimido (JAR) obtenido de los mismos archivos .class.



Inicialmente, los archivos de clases se convirtieron en archivos dex utilizando el compilador DX incorporado. Pero desde Android Studio 3.1 en adelante, D8 se convirtió en el compilador predeterminado . En comparación con el compilador DX, D8 compila más rápido y genera archivos dex más pequeños, al tiempo que proporciona un mejor rendimiento de la aplicación en tiempo de ejecución. El código de bytes dex obtenido de esta manera se minimiza utilizando la utilidad ProGuard de código abierto . Como resultado, obtenemos el mismo archivo dex, pero más pequeño. Este archivo dex se usa para construir el apk y finalmente para implementarlo en un dispositivo Android.







Pero detrás de D8 en 2018 vino R8, que, de hecho, es el mismo D8, solo que con adiciones.



Cuando se trabaja con Android Studio 3.4 y el complemento Android Gradle 3.4.0 o superior, Proguard ya no se usa para optimizar el código en tiempo de compilación. En cambio, el complemento funciona de forma predeterminada con R8, que reduce, optimiza y ofusca el código. Aunque R8 solo ofrece un subconjunto de la funcionalidad proporcionada por Proguard, le permite hacer la conversión de código de bytes de Java a código de bytes dex una vez, lo que reduce aún más el tiempo de compilación.







R8 y reducción de código





Normalmente, las aplicaciones utilizan bibliotecas de terceros como Jetpack, Gson, Google Play Services. Cuando usamos una de estas bibliotecas, a menudo la aplicación usa solo una pequeña porción de cada biblioteca individual. Sin encogimiento de código, todo el código de la biblioteca se almacena en su aplicación.



Sucede que los desarrolladores usan código detallado para mejorar la legibilidad y el mantenimiento de una aplicación. Por ejemplo, se pueden usar nombres de variables significativos y un patrón de diseño para ayudar a otros a comprender el código más fácilmente. Pero las plantillas tienden a generar más código del necesario.



Aquí es donde el R8 viene al rescate. Le permite reducir significativamente el tamaño de la aplicación, optimizando el tamaño incluso del código que realmente utiliza la aplicación.



Como ejemplo, a continuación se muestran los números del informe Reducir su aplicación con R8 , que se presentó en la Cumbre de desarrolladores de Android '19:







Y así es como se veía la comparación de la efectividad de R8 en la etapa de lanzamiento beta (tomado de la fuente Blog de desarrolladores de Android ):













Se pueden encontrar más detalles en la documentación e informe de la oficina .



ART vs DVM en Android



DVM fue diseñado específicamente para dispositivos móviles y se utilizó como una

máquina virtual para ejecutar aplicaciones de Android hasta Android 4.4 Kitkat.



A partir de esta versión, ART se introdujo como un tiempo de ejecución, y en Android 5.0 (Lollipop) ART reemplazó completamente a Dalvik.



La principal diferencia clara entre ART y DVM es que ART usa la compilación AOT, mientras que DVM usa la compilación JIT. No hace mucho, ART comenzó a utilizar un híbrido de AOT y JIT. Echemos un vistazo más de cerca a esto.



DVM



  • Utiliza la compilación JIT: siempre que se inicia la aplicación,
  • se compila la parte del código que se necesita para ejecutar la aplicación. El resto del código se compila dinámicamente. Esto ralentiza el inicio y el funcionamiento de las aplicaciones, pero reduce el tiempo de instalación.
  • , .
  • , DVM, , , ART.
  • , CPU.
  • Dalvik “” 4.4.






ART



  • AOT , . , .
  • , .
  • AOT , DVM.
  • , - .
  • Recolección de basura mejorada o recolección de basura. Al usar Dalvik, los recolectores de basura tuvieron que hacer 2 pases de pila, lo que condujo a una UX deficiente. En el caso de ART, no existe tal situación: limpia el montón una vez para consolidar la memoria.






Y un pequeño diagrama de Dalvik vs ART:





JIT + AOT en ARTE



Android Runtime (ART) desde Android 7 incluye un compilador JIT con creación de perfiles de código. El compilador JIT complementa al compilador AOT y mejora el rendimiento en tiempo de ejecución, ahorra espacio en disco y acelera las actualizaciones de aplicaciones y sistemas.



Esto sucede de la siguiente manera: en





lugar de ejecutar la compilación AOT de cada aplicación durante la fase de instalación, ejecuta la aplicación bajo el control de una máquina virtual usando un compilador JIT (casi lo mismo que en Android <5.0), pero realiza un seguimiento de cuál las secciones del código de la aplicación se ejecutan con mayor frecuencia. Esta información luego se utiliza para compilar estas secciones de código. La última operación se realiza solo cuando el teléfono inteligente está inactivo mientras se está cargando.



En términos simples, ahora dos enfoques completamente diferentes funcionan juntos, lo que brinda sus ventajas:



  • compilación más eficiente: cuando se ejecuta una aplicación en tiempo real, el compilador tiene la oportunidad de aprender mucho más sobre su trabajo que realizar análisis estáticos y, como resultado, se aplican métodos de optimización más apropiados para cada situación;
  • preservación de RAM y memoria permanente: el código de bytes es más compacto que el código de máquina, y si realiza la compilación AOT de solo ciertas secciones de la aplicación y no compila aplicaciones que el usuario no usa, puede ahorrar significativamente espacio de memoria NAND;
  • aumento dramático en la instalación y la primera velocidad de arranque después de la actualización del sistema: sin compilación AOT, sin demoras.


Lea más sobre la implementación del compilador JIT en ART aquí .



Conclusión



En este artículo, intenté echar un vistazo a las principales diferencias entre Dalvik y ART y, en general, observar cómo Android ha mejorado sus herramientas de desarrollo a lo largo del tiempo.



ART todavía está en desarrollo con nuevas características que se agregan para mejorar la experiencia tanto para los usuarios como para los desarrolladores.



Si fue útil, hágamelo saber en los comentarios.



All Articles