¿Qué es la máquina virtual de Java o Java Virtual Machine?
Menú de navegaciónMenú
Categorías

La mejor forma de Aprender Programación online y en español www.campusmvp.es

¿Qué es la máquina virtual de Java o Java Virtual Machine?

Cabecera

Los lenguajes de programación de alto nivel como C, C++, Java o Python, por citar unos pocos, sirven para comunicarse con una computadora mediante algo más fácil de entender para un humano que el "lenguaje máquina" o el lenguaje ensamblador, que está muy cercano a la máquina. Estos lenguajes se llaman "de alto nivel" porque están en un nivel de abstracción mucho mayor que el que ofrece un ordenador: tienen bucles, condicionales, matrices, tipos de datos...

Tradicionalmente, el compilador de un lenguaje de alto nivel se encargaba de traducir ese lenguaje "sencillo" en lenguaje máquina, directamente utilizable por el computador a través del sistema operativo. Es decir, cuando compilamos un programa en C++ lo que obtenemos es un programa ejecutable, por ejemplo para Windows, que este sistema operativo es capaz de ejecutar directamente contra el procesador, en un lenguaje "entendible" por este.

Sin embargo muchos lenguajes modernos como Java o C# (y otros lenguajes de la plataforma .NET), lo que hacen es utilizar un paso intermedio entre estos dos estados: entre el código de alto nivel en el que escribimos las aplicaciones y el de bajo nivel que sale del proceso de compilación.

Cuando compilas una aplicación escrita en lenguaje Java, en realidad éste no se compila a lenguaje máquina, directamente entendible por el sistema operativo, sino a un lenguaje intermedio denominado Byte Code. Lo mismo ocurre con las aplicaciones .NET que se compilan también a un lenguaje intermedio llamado MSIL (MicroSoft Intermediate Language).

Entre el Byte Code (o el MSIL en el caso de .NET) y el sistema operativo se coloca un componente especial llamado Máquina virtual que es el que realmente va a ejecutar el código. Esta idea, por cierto, no tiene nada que ver con las máquinas virtuales a las que estamos acostumbrados hoy en día que ejecutan sistemas operativos completos, sino que es un concepto mucho más antiguo. Es más, tampoco te vayas a pensar que esto lo inventó Java. Ni mucho menos. Ya en los años '80, mucho antes de Java, el lenguaje Pascal generaba p-Code que era algo muy similar, e incluso había compiladores de COBOL (¡sí, de COBOL!) que hacían lo mismo.

En el caso de Java, La Java Virtual Machine o JVM toma el código Byte Code resultante de compilar tu aplicación Java y lo compila a su vez a código nativo de la plataforma en la que se está ejecutando. La ventaja principal de este esquema es que es muy fácil crear un programa en Java y que luego éste se pueda ejecutar en cualquier sistema operativo para el cual exista una implementación de la JVM (hoy en día, casi literalmente todos).

El siguiente esquema ilustra bien cuál es este proceso:

Esquema de funcionamiento de la compilación Java

Si lo necesitas, puedes repasar en este enlace los conceptos básicos de la plataforma Java.

Para entendernos, la JVM es una abstracción de una máquina real, que es capaz de entender el Byte Code creado por el compilador de Java y traducirlo en instrucciones nativas equivalente que a su vez el sistema operativo actual es capaz de entender, ejecutando realmente la aplicación.

Existen implementaciones de la JVM para prácticamente la totalidad de sistemas operativos del mercado, no solo para los tres que hemos visto en la figura anterior. Por eso, en la práctica, los programas Java se pueden ejecutar en teoría en cualquier sitio y de ahí su famoso eslogan "Escribe una vez, ejecuta en todas partes".

El secreto del "Write once, write everywhere" es, precisamente, la existencia de la JVM, que es la base de toda la filosofía de la plataforma Java.

La Java Virtual Machine, al igual que su contrapartida real, ejecuta los programas como si fuera una computadora, para lo cual utiliza diversos componentes, del mismo modo que los usaría un procesador real. Entre los componentes más importantes se encuentran los registros, la pila, el recolector de basura... En la siguiente figura, basada en una que aparece en la documentación oficial, podemos los más importantes:

Componentes de la JVM

No vamos a entrar en los detalles de cada uno de los componentes. Para esto tienes la documentación oficial y además es un tema complejo. Pero sí que es interesante conocer un poco mejor uno de ellos: el compilador JIT.

El Compilador Just In-Time o JIT

La ejecución del Byte Code es lenta comparada con la del código nativo en cada plataforma. La JVM primero tiene que cargar el código y traducirlo a instrucciones nativas para poder ejecutarlo, haciendo otras muchas cosas por el medio, como comprobar los tipos, recoger basura, etc...

Una manera de optimizarlo y conseguir un alto rendimiento es hacer una compilación inmediata, lo antes posible, al código nativo de cada plataforma, cacheando además los resultados para reutilizarlos. Esto es básicamente lo que hace este componente, cuyo nombre viene de esta función: Just In-Time Compiler o compilador en tiempo real.

OJO, esto no quiere decir que compile todo el código a código nativo (para eso tendríamos un compilador para cada plataforma, como ocurre con C++, por ejemplo). Lo que hace la JVM es decidir en cada momento qué partes del código se deben compilar con el JIT y cuáles almacenar, cuándo ejecutarlas, etc...

Dado que esa compilación también lleva tiempo, el compilador JIT suele compilar a código nativo el código que se use con mayor frecuencia, pudiendo notar una pequeña demora en la ejecución la primera vez que se compila.

De hecho según la implementación concreta de la JVM, el proceso del compilador JIT puedes estar más o menos optimizado. Por ejemplo, en el caso de JRockKit, la JVM oficial de Oracle, el optimizador para JIT se ejecuta en segundo plano todo el tiempo analizando el uso que se hace del Byte Code y optimizando todo el tiempo, en función de las circunstancias, qué partes de la aplicación se compilan a código máquina. Además todo el tiempo, en función de la ejecución real, se realizan optimizaciones sobre el código original para generar posteriormente a través del compilador JIT nuevo código máquina súper-optimizado. En el artículo Understanding Just-In-Time Compilation and Optimization puedes encontrar más detalles.

El concepto de compilador JIT es posterior al de la JVM y sistemas similares, ya que de hecho se inventó para mejorar el rendimiento de éstos.

En resumen

Para resumir en cuatro puntos clave todo lo explicado anteriormente:

  • Los programas Java se compilan a un formato binario .class que contiene instrucciones en un lenguaje de bajo nivel especial independiente de la plataforma, llamado Byte Code.
  • Existe un componente de Java que debe estar instalado en cada sistema operativo que se llama Java Virtual Machine o JVM. La JVM es capaz de entender el Byte Code y ejecutarlo con las instrucciones equivalentes que haya en el sistema operativo y procesador actuales en el que estemos ejecutándolo.
  • Esto es una gran ventaja porque nos permite ejecutar código Java en cualquier plataforma para la que exista una JVM (en la práctica, casi cualquiera).
  • Dado que la ejecución mediante la JVM es más lenta que la nativa, en ocasiones puede notarse cierta lentitud. Para ello un componente especial de la JVM llamado Compilador JIT o Compilador en Tiempo Real, compila a código nativo las partes de código más comunes o que requieren mayor desempeño.

¡Espero que te resulte útil!

José Manuel Alarcón Director de campusMVP, es ingeniero industrial y especialista en consultoría de empresa. Ha escrito diversos libros, habiendo publicado hasta la fecha cientos de artículos sobre informática e ingeniería en publicaciones especializadas. Puedes seguirlo en Twitter en @jm_alarcon o leer sus blog técnico o personal. Ver todos los posts de José Manuel Alarcón

No te pierdas ningún post

Únete gratis a nuestro canal en Telegram y te avisaremos en el momento en el que publiquemos uno nuevo.

Archivado en: Lenguajes y plataformas

Agregar comentario