Conceptos básicos de JVM (1): conozca la máquina virtual

Prefacio

Encantado de conocerte ~ Bienvenido a leer mi artículo.

JVM es un obstáculo que todo programador de Java debe superar porque es muy importante. El conocimiento subyacente de Java, en el análisis final, es el conocimiento relacionado con JVM. Muchos lectores sienten que cuando ven JVM: Oh, este es el conocimiento de bajo nivel, aprendamos primero la capa de aplicación. O cuando se trata de la entrevista, tienes que recitar algunas preguntas. En mi opinión, comprender la JVM es la única forma de comprender todo el ecosistema de Java. El desarrollo de lenguajes de programación, desde la cadena de código máquina 01 hasta el actual lenguaje Java de alto nivel, ha condensado la sabiduría de innumerables pioneros. Comprender JVM no es solo para entrevistas, sino también para sentir la sabiduría de los predecesores. Aprender JVM también puede hacer que nuestros programas Java sean más sólidos.

En cuanto a los investigadores que no pertenecen a JVM, tal vez no necesitemos hacer una investigación en profundidad, y comprender su modelo conceptual es suficiente. Como ingeniero de Android, JVM también es uno de los que debe aprender. Serie de artículos sobre conceptos básicos de JVM, principalmente sobre el conocimiento básico clave que JVM necesita conocer, y no profundizará en el conocimiento subyacente de JVM. Para un ingeniero de Android, estos son definitivamente suficientes. Los lectores interesados ​​en JVM pueden continuar estudiando en profundidad después de leerlo. Creo que esta serie de artículos también puede proporcionarle una guía para aprender JVM.

En el primer artículo, hablemos de JVM. ¿Por qué JVM es un gran paso para los humanos en los lenguajes de programación? ¿Qué papel juega JVM en el funcionamiento de todo el programa Java? JVM está escrito en lenguaje C, entonces, ¿es el lenguaje C un lenguaje multiplataforma? Si C es un lenguaje multiplataforma, ¿por qué desarrollar Java? ¿No es C más rápido? ¿Por qué Java es tan popular, pero el lenguaje C todavía no ha caído? Con estas preguntas en mente, este artículo hablará sobre la breve historia del desarrollo de los lenguajes informáticos y verá cómo el código máquina evolucionó a Java paso a paso. Si comprende este conocimiento, podrá comprender mejor la JVM.

El nacimiento del lenguaje C

Sabemos que el hardware de la computadora solo conoce la cadena 01 de código de máquina. En los primeros días del desarrollo informático, los programadores escribían programas escribiendo 01 cadena. Por ejemplo, un casete estenopeico es una de las formas:

Por supuesto, las computadoras en ese momento no eran las mismas que las nuestras hoy, y las computadoras en ese momento solo podían realizar algunas operaciones muy simples. Por ejemplo, necesitamos calcular: 4 + 8, luego las instrucciones de cálculo deben tener :, 0100,1000,1101donde la primera cadena binaria representa el número 4, la segunda representa el número 8 y la tercera representa las instrucciones de suma (por supuesto, estas instrucciones son no existente, solo para dar un ejemplo). Esta forma de escribir programas no solo es una semántica de código ineficiente, complicada y poco clara, y no es adecuada para el desarrollo de programas cada vez más complejos . Entonces, los pioneros pensaron en una forma, ¿pueden tomar un "nombre" para estas instrucciones, por ejemplo 1101, escribirlas como add, y luego usar un compilador para addmapearlas 1101, de modo que la próxima vez que escriba 4 + 8, pueda escribirlas 4,8,addcomo Nuestra configuración se convierte a 0100,1000,1101, por lo que no es muy conveniente. Este es el origen del lenguaje ensamblador.

Como se mencionó anteriormente, no solo podemos escribir operaciones de suma como instrucciones, sino también escribir restas, asignaciones, bucles, bifurcaciones, etc. como instrucciones, y finalmente compilarlas en código máquina. En comparación con el uso directo de código de máquina para escribir programas, el uso de lenguaje ensamblador para escribir programas mejora en gran medida la eficiencia del desarrollo y es adecuado para el desarrollo de programas más complejos, con una semántica de código más clara .

Diferentes máquinas tienen diferentes instrucciones, por ejemplo, la instrucción de suma de la máquina A es 0010, mientras que la máquina B es 0101. Existe una correspondencia uno a uno entre el lenguaje ensamblador y el código de máquina de la CPU, por lo que diferentes máquinas tienen diferentes lenguajes ensambladores específicos. Con el desarrollo de las computadoras, los escenarios comerciales se han vuelto cada vez más complejos y el lenguaje ensamblador también ha comenzado a aparecer más que suficiente. En este momento, se necesita un lenguaje de programación más avanzado que el ensamblador para mejorar la eficiencia del desarrollo del programador. En este momento nació el lenguaje C. (Por supuesto hay una lengua B en el medio, así como algunos otros detalles, aquí hablamos principalmente de la evolución general de la lengua, sin entrar en detalles).

Similar al lenguaje ensamblador formado por código máquina, el lenguaje C también tiene un mayor nivel de abstracción al lenguaje ensamblador. A través del compilador de lenguaje C, puede compilar lenguaje C en lenguaje ensamblador. De esta forma, se mejora una vez más la eficiencia del trabajo y se completa una lógica empresarial más compleja. Como se muestra abajo:

Como mencionamos anteriormente, el lenguaje ensamblador de diferentes plataformas es diferente, por lo que en diferentes plataformas, es necesario escribir diferentes compiladores de lenguaje C para compilar el lenguaje C en el lenguaje ensamblador correspondiente.

Por lo tanto, para ejecutar un programa en lenguaje C en una máquina, primero use un compilador de lenguaje C para compilar el lenguaje C en el lenguaje ensamblador de la máquina, y luego use el compilador de lenguaje ensamblador de la máquina para compilar el lenguaje ensamblador en código máquina. De esta forma, se puede ejecutar el programa c.

¿Fue primero la gallina o el huevo?

Sabemos que los compiladores de lenguaje C están escritos principalmente en lenguaje C, entonces, ¿en qué se escribió el primer compilador de lenguaje C del mundo? La gallina o el huevo fue lo primero ...

De hecho, el lenguaje C y el compilador de lenguaje C no se volvieron tan perfectos como lo son hoy. El primer compilador del lenguaje C se escribió en lenguaje ensamblador, pero solo realizó las funciones más básicas del lenguaje C , como los tipos de datos básicos, las operaciones básicas y el control de código básico. De esta forma, la tarea del compilador es menor y el lenguaje ensamblador se puede implementar más rápido. Luego, construya sobre el lenguaje C que se ha implementado, use el lenguaje C para continuar desarrollando las características del lenguaje C y enriquezca continuamente las funciones del compilador. Por lo tanto, existen C0, C1, C2 ... Varias versiones del lenguaje C, las versiones están en constante iteración y las características del lenguaje C son cada vez más útiles. El progreso del lenguaje C también ha promovido su propio desarrollo, lo que equivale a un círculo virtuoso.

De manera similar al desarrollo del lenguaje ensamblador al lenguaje C, también podemos continuar desarrollando lenguajes más avanzados sobre la base del lenguaje C para satisfacer nuestro desarrollo, como Java. El llamado 0 produce 1, 1 produce C y C produce todo. ¿Por qué los estudiantes de informática necesitan aprender primero el lenguaje C? Porque el lenguaje C es el creador y la base de todos los lenguajes de programación de alto nivel en la actualidad. Los lectores interesados ​​pueden leer este artículo para un análisis en profundidad: Dado que el compilador de C está escrito en lenguaje C, ¿cómo surgió el primer compilador de C? entender más.

¿Puede el lenguaje C ser multiplataforma?

Escribimos un programa en lenguaje C en el sistema operativo de la ventana, luego usamos el compilador, hacemos clic en ejecutar y el programa se ejecutará. Copiamos el código, luego lo compilamos en el sistema Linux y también se puede ejecutar. Entonces, ¿esto prueba que el lenguaje C es compatible en diferentes plataformas, es decir, el lenguaje C es multiplataforma?

¡La respuesta es no! Primero, tenemos que aclarar el concepto de multiplataforma. Multiplataforma se refiere a que el idioma no depende del sistema operativo o del entorno de hardware. Las aplicaciones desarrolladas bajo un sistema operativo aún pueden ejecutarse bajo otro sistema operativo . La clave de que el lenguaje C se puede compilar y ejecutar en diferentes plataformas es: compilador , debemos escribir compiladores para diferentes plataformas de lenguaje C, y luego usar el compilador para compilar el lenguaje C en el código de máquina correspondiente, luego este programa de lenguaje C puede ejecutarse No puede ser independiente de la plataforma, por lo que el lenguaje C no es un lenguaje multiplataforma .

Entonces, simplemente escriba un compilador en lenguaje C para diferentes plataformas, ¿se puede ejecutar el mismo programa en lenguaje C en diferentes plataformas? ¡La respuesta es no! Como sabemos antes, el lenguaje C está realmente relacionado con el lenguaje ensamblador de la plataforma específica, y el lenguaje ensamblador está relacionado con las instrucciones de máquina de la máquina específica, por lo que el lenguaje C será diferente en diferentes plataformas. Por ejemplo, la variable entera int puede tener 16 bits o 32 bits en diferentes máquinas. Los lectores interesados ​​pueden leer este artículo C ¿por qué no multiplataforma para obtener más información?

Protagonista multiplataforma: JVM

Como se mencionó anteriormente, el lenguaje C no puede ser multiplataforma. Cada programa de lenguaje C debe ser compilado en el código de máquina de la máquina correspondiente por un compilador para su ejecución. Para lograr la multiplataforma, necesitamos tener un programa A, que pueda compilar dinámicamente el código en el código de máquina de una plataforma específica cuando ** se está ejecutando **, para que el mismo programa se pueda ejecutar directamente en diferentes plataformas sin primero Compilar el programa en la plataforma correspondiente. Este programa A es una máquina virtual. La máquina virtual aquí no se refiere a la máquina virtual Java, sino a la máquina virtual en general, y la JVM es solo un tipo de máquina virtual. Lo que debe lograr la máquina virtual es: no es necesario compilar un programa y el programa se puede ejecutar directamente . La máquina virtual puede tratar el programa que escribimos como sus propias instrucciones de ejecución, y luego ejecutarlo línea por línea, sin necesidad de convertirlo todo en el código de máquina de la plataforma correspondiente.

JVM, el nombre completo es Java Virtual Machine (Java Virtual Machine), pero no es compatible con el lenguaje Java, de hecho, JVM solo reconoce archivos de clase. Los programas Java que escribimos deben compilarse antes de que puedan ejecutarse en la máquina virtual. El resultado compilado del archivo Java se denomina bytecode, que es el archivo de clase. El archivo de clase es el único código que reconoce la máquina virtual, al igual que la máquina solo reconoce el código de máquina, por lo que el archivo de clase también se puede llamar el "código de máquina" de la máquina virtual. Como se muestra abajo:

JVM es esencialmente un programa escrito en lenguaje C. Este programa tiene una función similar a la de un compilador de lenguaje C: compila lenguaje de alto nivel en código máquina para la plataforma correspondiente. Pero la diferencia es que el compilador de lenguaje C debe compilar completamente el programa C antes de ejecutarlo, mientras que la JVM puede ejecutar directamente el archivo de clase y compilarlo en código de máquina mientras lo ejecuta . Cada plataforma tiene una máquina virtual diferente, y el principio de tener diferentes compiladores de lenguaje C para diferentes plataformas es el mismo que mencionamos anteriormente. Si una plataforma quiere ejecutar un programa Java, primero debe configurar el entorno Java, es decir, instalar el JRE (Java Runtime Environment) El entorno de ejecución Java se puede considerar como un programa JVM instalado en la plataforma. Cuando ejecutamos el programa de archivo de clase en una plataforma, primero iniciamos la máquina virtual, la máquina virtual carga el archivo de clase y luego se ejecuta. Por lo tanto, después de que el programa Java que escribimos se compila en un archivo de clase unificado, solo si la plataforma está instalada con una máquina virtual, podemos ejecutar directamente nuestro programa, logrando así multiplataforma.

Entonces, hasta ahora, parece que la máquina virtual es realmente como una "máquina", pero su código de máquina no es una cadena 01, sino un archivo de clase. JVM tiene su propia división de memoria, como pila de métodos, área de pila, grupo constante, etc. En la operación real, la máquina virtual también compila archivos de clase en código de máquina para ejecutar en la máquina real. A través de la máquina virtual, estamos protegidos de la máquina real y vemos una máquina virtual. Nuestro desarrollo ya no necesita preocuparse por la máquina específica, solo la JVM.

En este punto, no sé si el lector sentirá curiosidad: ¿Por qué la máquina virtual no ejecuta directamente programas Java, sino que primero debe compilar Java en código de bytes (es decir, archivos de clase) antes de ejecutarlo? ¿No es superfluo? Este es en realidad otro objetivo de diseño de la JVM: la independencia del idioma . Como se mencionó anteriormente, la existencia de JVM hace posible ejecutar directamente archivos de clase, es decir, código de bytes, en las principales plataformas, logrando la independencia de la plataforma. Y cualquier lenguaje de programación de alto nivel, siempre que se pueda compilar en un archivo de clase, este lenguaje se puede ejecutar en la JVM, para darse cuenta de la independencia de plataforma de la JVM. Por ejemplo, Groovy y Kotlin, todos se pueden compilar en archivos de clase, luego también se pueden ejecutar en la JVM, y estos lenguajes también se pueden llamar colectivamente: lenguajes JVM. Como se muestra abajo:

Por lo tanto, para ser más precisos, no debería llamarse una máquina virtual Java, sino una máquina virtual de clase.

¿Por qué no ha disminuido el lenguaje c?

Hoy en día, cuando los lenguajes multiplataforma como el lenguaje Java están en pleno apogeo, ¿por qué no ha disminuido un lenguaje compilado como el lenguaje C? En cambio, ¿ocupar el primer lugar en la clasificación de lenguajes de programación durante todo el año? Esto implica dos costos importantes que una máquina virtual debe pagar: velocidad y entorno .

Primero mira el primer factor. La máquina virtual ejecuta directamente el código intermedio, que se ve muy bien, pero de hecho necesita interpretar el código intermedio como código de máquina antes de que realmente pueda ejecutarse. Por ejemplo, el código intermedio de JVM es un archivo de clase. Entonces, el proceso de ejecución implica el proceso de interpretación del código intermedio, y el programa ejecutable en sí es código de máquina, que se puede ejecutar directamente, y la diferencia de velocidad entre los dos es muy grande. Por qué el sistema iOS siempre se siente más fluido y rápido que Android, la máquina virtual es un factor muy importante. El programa IOS se compila directamente en un programa ejecutable, que es rápido, pero también paga el precio de no poder multiplataforma. Pero, ¿pueden los programas de iOS ejecutarse en otros sistemas? De hecho, creo que la elección de Google del lenguaje JVM como lenguaje de desarrollo de Android no es la mejor opción. El programa de Android que desarrollamos no es lo suficientemente práctico como para ser multiplataforma. Nuestro programa solo se ejecutará en el sistema Android, no en otros sistemas y, de hecho, no se puede ejecutar en otros sistemas. Nuestro programa necesita llamar a la API del sistema Android y necesita interactuar con el sistema, que en sí mismo ha determinado que debemos ejecutarlo en el sistema Android. La elección del lenguaje JVM para desarrollar Android trae una característica multiplataforma insignificante, pero a un precio de rendimiento enorme.

El segundo factor es el medio ambiente. El lenguaje JVM requiere que JVM se ejecute y una plataforma debe instalar JRE antes de ejecutar programas. Entonces, para algunas máquinas pequeñas, como relojes, etc., la memoria es muy pequeña y la capacidad de la CPU también es muy limitada. El costo de la memoria y el consumo de rendimiento que aporta JVM superan a estas máquinas. En este momento, un lenguaje compilado como C es la mejor opción.

Hay otro precio que agregar a la JVM: el código de la JVM no puede manipular la memoria directamente . Cuanto más avanzado y abstracto es un lenguaje, menor es la correlación entre él y la máquina. Cuanto más te acerques a las personas, más te alejarás de las máquinas. Los lenguajes como C / C ++ tienen capacidades muy poderosas: memoria operativa. En el análisis final, también evolucionaron a partir del código de máquina, por lo que son muy adecuados para algunos programas embebidos o programas que necesitan manipular la memoria de la máquina, como JVM. Los lenguajes de alto nivel como Java son impotentes. Por supuesto, también hay JVM escritas en programas Java, pero de hecho, es lo mismo que el principio del "huevo o la gallina" que discutimos antes, y todavía se necesita la ayuda del lenguaje C.

En resumen, los lenguajes multiplataforma y los lenguajes compilados directamente son aplicables a diferentes escenarios. De ninguna manera es un lenguaje multiplataforma que derrota al lenguaje C, sino otro lenguaje mejor compilado, pero por ahora, no puede aparecer en un futuro cercano.

Familia de máquinas virtuales

El contenido anterior está más sesgado hacia la máquina virtual en un sentido amplio Esta parte está dedicada a hablar sobre la máquina virtual Java.

Como se mencionó anteriormente, hay más de un tipo de máquina virtual, de hecho, hay más de un tipo de máquina virtual Java. La máquina virtual principal actual, es decir, la máquina virtual utilizada por Sun / OracleJDK y OpenJDK, es HotSpot. HotSpot fue desarrollado originalmente por una pequeña empresa y luego fue adquirido y desarrollado por Sun. Después de que Oracle adquirió Sun, integró las excelentes características de la máquina virtual JRockit de BEA en HotSpot. Al mismo tiempo, debido a que Sun / OracleJDK es dominante en las aplicaciones Java, HotSpot también se conoce como la máquina virtual más utilizada. Además, están la máquina virtual JRockit de BEA antes mencionada, la máquina virtual IBM J9 y así sucesivamente. El primero fue adquirido por Oracle e integrado en HotSpot, y el segundo aún está muy activo, pero aún es relativamente pequeño en comparación con HotSpot.

Android ejecuta programas Java y, naturalmente, existen máquinas virtuales. Sin embargo, la máquina virtual de Android no se puede llamar JVM, porque no cumple con las especificaciones de la máquina virtual de Java, sino que se ha transformado de acuerdo con las características del terminal móvil. Se basa en una arquitectura de registro en lugar de una arquitectura de pila. Al mismo tiempo, la máquina virtual de Android no ejecuta un archivo de clase o un archivo jar, sino un archivo dex, pero el archivo dex se puede transformar a partir de los dos primeros. La primera máquina virtual de Android fue la máquina virtual Dalvik. Para mejorar el rendimiento, se utilizó el compilador JIT (Just In Time Compiler) para compilar el código de bytes. La característica de JIT es: interpretar el hot code durante su uso, es decir, el código que se usa con más frecuencia, como código máquina, para que la próxima vez que se ejecute en este lugar se pueda aumentar la velocidad. Es decir, cuanto mayor sea el tiempo de ejecución, más fluida será la aplicación. Después de Android 5.0, la máquina virtual Dalvik fue reemplazada por la máquina virtual ART. La característica de ART es AOT (compilación anticipada), es decir, compilación anticipada. Puede compilar todo el código en código de máquina al instalar la aplicación, de modo que el rendimiento general se mejora muy bien. Pero ha pagado un precio muy alto: un tiempo de instalación prolongado y una cierta cantidad de espacio para almacenar el código de la máquina. Luego, en Andoid7.0, JIT también se agregó a la máquina virtual ART, de modo que al instalar, solo una pequeña parte debe compilarse por adelantado, y cuando se ejecuta, cuando encuentra un lugar que no ha sido compilado. , luego use JIT para compilar.

para resumir

Este artículo explica el proceso de desarrollo de los lenguajes de programación y la clave para la realización multiplataforma: máquinas virtuales y, finalmente, analiza las características de los lenguajes multiplataforma y los lenguajes compilados y la familia de máquinas virtuales.

Como clave de los lenguajes multiplataforma, las máquinas virtuales nos permiten aprender conocimientos que no se pueden ignorar en los lenguajes multiplataforma. Antes de aprender a usar máquinas virtuales, es necesario comprender los antecedentes de las máquinas virtuales y los problemas que resuelven las máquinas virtuales. Hoy en día podemos usar Java para desarrollar todo tipo de aplicaciones complejas, pero también por el arduo trabajo de desarrollar una máquina virtual, nuestros predecesores nos ayudaron a hacerlo y seguimos mejorando constantemente. Esta es la cristalización de la sabiduría de decenas de millones de predecesores en las últimas décadas. Con el asombro del conocimiento, es más digno de nuestro estudio.

Este es el texto completo. No es fácil ser original. Si es útil, puedes darle me gusta, agregarlo a favoritos, comentarlo y reenviarlo.
El autor tiene una capacidad limitada. Si tiene alguna idea, no dude en comunicarse y corregir en el área de comentarios.
Si necesita reimprimir, envíe un mensaje privado.

También bienvenido a mi blog personal: Portal

Supongo que te gusta

Origin blog.csdn.net/weixin_43766753/article/details/109199647
Recomendado
Clasificación