La JVM es la máquina virtual que carga y ejecuta el bytecode de Java. Es la pieza que permite usar el mismo programa compilado en plataformas distintas.
Cuando empiezas con Java, lo primero con lo que te chocas es con una sopa de letras: JVM, JRE, JDK. ¿Qué instalo? ¿Qué necesito para solo ejecutar? ¿Y para programar?
Vamos a abrir el capó de Java para entender su arquitectura y distinguir las responsabilidades de la JVM, el JRE y el JDK.
Entender esta separación ayuda a diagnosticar errores como “javac no se reconoce como un comando interno” y a elegir qué instalar.
JDK, JRE y JVM
Para entender la relación entre estos tres componentes, lo mejor es imaginarlos como una muñeca rusa (Matrioshka) o capas de cebolla. Uno contiene al otro.
JVM: la máquina virtual
Es la pieza central. La JVM es una “máquina imaginaria” que vive dentro de tu ordenador.
Tu procesador (Intel, AMD, ARM) entiende unas instrucciones concretas (código máquina). Java no le habla directamente a tu procesador. Java le habla a la JVM, y es la JVM la que traduce esas órdenes al procesador de turno.
- Su función: Cargar el código, verificarlo y ejecutarlo.
- El detalle: La JVM sí es dependiente de la plataforma. Hay una JVM específica para Windows, una para Linux y una para macOS.
JRE: el entorno de ejecución
La JVM por sí sola no sirve de mucho; es como un motor sin ruedas. Necesita librerías para funcionar (por ejemplo, para abrir ficheros, conectar a internet o pintar en pantalla).
El JRE es el conjunto de la JVM + Librerías Estándar de Java (el API de Java).
- ¿Para quién es? Conceptualmente, para quien solo necesita ejecutar una aplicación. Desde Java 11 ya no se publica un JRE independiente dentro de OpenJDK; las aplicaciones modernas suelen incluir un runtime reducido o piden instalar un JDK.
JDK: el kit de desarrollo
Para desarrollar usamos el JDK, que reúne el runtime y las herramientas de desarrollo.
El JDK contiene el JRE (que a su vez tiene la JVM), y además añade herramientas de desarrollo. Las más importantes son:
javac: El compilador (convierte tu código.javaen.class).javadoc: Generador de documentación.jdb: El depurador (debugger).
Resumen rápido:
- Si eres usuario: Normalmente la aplicación incluye su propio runtime.
- Si eres programador: Instalas JDK.
El proceso de compilación: el bytecode
En lenguajes como C++, cuando compilas, generas un archivo .exe (en Windows) que contiene código binario que el procesador entiende directamente. Si te llevas ese .exe a Linux, no funciona.
En Java, el proceso es diferente. Cuando usas el compilador javac sobre tu archivo Main.java, no obtienes código máquina. Obtienes un archivo Main.class que contiene Bytecode.
¿Qué es el bytecode?
El Bytecode es un lenguaje intermedio. Es un código binario compacto que no está pensado para un procesador real, sino para ser entendido por la JVM.
Es como si escribieras un libro en Esperanto. Nadie lo habla nativamente, pero con un traductor (la JVM) cualquiera puede entenderlo.
Una vez tienes el Bytecode, puedes llevártelo a cualquier sistema operativo. Allí, la JVM instalada lo leerá y lo traducirá a las instrucciones nativas de esa máquina.
¿Es Java interpretado o compilado? (JIT)
Aquí viene la parte técnica interesante. Históricamente, se decía que Java era “lento” porque era interpretado.
Al principio, la JVM leía el Bytecode instrucción por instrucción y la traducía al vuelo. Esto era ineficiente comparado con C++. Pero hace muchos años que esto cambió gracias al JIT (Just-In-Time Compiler).
El compilador JIT
Las JVM modernas (como HotSpot) son muy listas. Funcionan así:
- Interpretación inicial: El programa arranca interpretando el Bytecode para empezar rápido.
- Profiling (Análisis): La JVM vigila qué partes del código se ejecutan más veces (los “puntos calientes” o HotSpots).
- Compilación en vivo: Cuando detecta que un método se usa mucho, el compilador JIT compila ese trozo de Bytecode a código máquina nativo y lo guarda en memoria.
- Optimización: A partir de ahí, las siguientes llamadas pueden ejecutar la versión nativa optimizada en lugar de interpretar de nuevo el bytecode.
Este proceso puede mejorar mucho el rendimiento de las aplicaciones de larga duración, ya que la JVM optimiza según el comportamiento observado durante la ejecución.