Ejecutables nativos en Quarkus: GraalVM, Mandrel y el runtime oculto

En el universo Java, que una aplicación inicie en pocos milisegundos con un consumo mínimo de memoria ha dejado de ser ciencia ficción. Gracias a GraalVM, Mandrel y sus tecnologías asociadas, hoy es posible hacerlo realidad.

En este artículo las desarmaremos para entender sus tecnologías, componentes, y cómo Quarkus puede empaquetar tu aplicación como un archivo ejecutable que corre sin una JVM instalada.


Resumen de componentes

Antes de empezar, aquí tienes una lista de los componentes principales:

Componente¿Qué es?Rol
GraalVMPlataforma de ejecución de Oracle LabsProporciona la JVM y herramientas de compilación nativa
MandrelDistribución de GraalVM por Red HatVersión optimizada para Quarkus, 100% open source
native-imageHerramienta de compilaciónTransforma tu app Java en un ejecutable nativo
SubstrateVMRuntime ligeroSe embebe directamente en el ejecutable para proporcionar funcionalidades esenciales sin JVM externa

¿Qué es GraalVM?

GraalVM es una plataforma de ejecución de alto rendimiento desarrollada por Oracle Labs. Aunque fue diseñada para soportar múltiples lenguajes, en el contexto de Java moderno destaca por algo revolucionario: la posibilidad de compilar aplicaciones Java como ejecutables nativos.

Puedes generar un único archivo ejecutable destinado a una arquitectura de hardware y sistema operativo específicos.

Una aplicación Java tradicional necesita:

  1. El archivo .jar con tu código
  2. Una JVM instalada en el entorno de ejecución

Con GraalVM, puedes obtener:

  1. Un único archivo ejecutable para una arquitectura y sistema operativo específicos.
    El ejecutable resultante no requiere una JVM instalada en el entorno de ejecución.

Esto es ideal para:

  • Entornos cloud-native (aplicaciones diseñadas para la nube)
  • Funciones serverless (que se ejecutan bajo demanda)
  • Contenedores donde cada megabyte cuenta

Esta capacidad de transformar cualquier aplicación Java en un ejecutable autocontenido y compacto se logra con un componente fundamental: native-image.


El papel de native-image

native-image es la herramienta que permite transformar tu aplicación Java y sus dependencias en un ejecutable completamente nativo.

Lo logra mediante un exhaustivo análisis estático del código durante la compilación:

  1. Examina tu aplicación a fondo
  2. Identifica las clases, métodos y recursos estrictamente necesarios
  3. Descarta cualquier elemento que no se use
  4. Genera un ejecutable optimizado

Los resultados:

LogroBeneficio
Arranques ultrarrápidosMilisegundos en lugar de segundos
Consumo mínimo de memoriaIdeal para contenedores
Comportamiento predecibleSin sorpresas del JIT en runtime
Compatibilidad nativaEjecutable directo en el SO objetivo

Pero native-image no está solo. Dentro de cada ejecutable nativo agrega un componente clave: SubstrateVM.


SubstrateVM: el runtime invisible

SubstrateVM es una máquina virtual nativa que forma parte de GraalVM, diseñada específicamente para ejecutar aplicaciones Java compiladas con native-image.

Piensa en SubstrateVM como un ‘motor de ejecución’ que se integra dentro del ejecutable nativo, permitiendo que la aplicación funcione sin una JVM instalada en el entorno de ejecución.

Su implementación está escrita mayormente en Java y se compila junto con tu aplicación durante el proceso de native-image. El resultado es un runtime nativo embebido en el ejecutable, que incluye solo los componentes realmente necesarios para interactuar con el sistema operativo.

A continuación se muestran algunas capacidades principales del runtime SubstrateVM que pueden quedar embebidas dentro del ejecutable, según lo que la aplicación requiera y lo que native-image determine como alcanzable durante el proceso de compilación.

Capacidad del runtime SubstrateVMDescripción
Gestión de memoriaManejo automático de memoria mediante un GC específico para imágenes nativas
Gestión de hilosSoporte básico de concurrencia sin el modelo completo de la JVM
APIs del JDK disponiblesInclusión selectiva de clases y paquetes del JDK que realmente se usan
Soporte de reflexión (opcional)Disponible únicamente si fue declarado explícitamente durante el build
Código de arranque del runtimeInicialización mínima del proceso y del entorno de ejecución

Estos elementos no son módulos configurables ni APIs públicas de SubstrateVM. Son partes internas del runtime que se incluyen (o no) automáticamente según el análisis de reachability realizado por native-image.

⚠️ Limitaciones importantes

Esta ligereza tiene un costo y es consecuencia directa del modelo de compilación nativa.

native-image trabaja bajo el supuesto de closed world, donde todo lo que se va a usar debe conocerse en tiempo de compilación.
Por este motivo, algunas características dinámicas de Java requieren configuración explícita o no están disponibles:

  • Reflexión dinámica: necesita registro explícito en build-time
  • Carga de clases en runtime: no soportada
  • Proxies dinámicos: requieren registro previo mediante anotaciones o archivos de configuración

SubstrateVM es, en definitiva, el motor silencioso que impulsa los ejecutables nativos.


¿Qué es Mandrel?

Mandrel es una distribución especializada y reducida de GraalVM, mantenida por Red Hat y centrada exclusivamente en la compilación nativa.

Diferencias con GraalVM

AspectoGraalVMMandrel
MantenedorOracle LabsRed Hat
Lenguajes soportadosJava, JavaScript, Python, Ruby, R, etc.Solo Java
BaseOpenJDK con mejoras de OracleOpenJDK estándar
LicenciaCommunity (open source) o Enterprise (componentes propietarios)100% open source
EnfoqueUso general, políglotaOptimizado para Quarkus
SoporteOracle (versión Enterprise)Red Hat (con RHBQ)

¿Por qué elegir Mandrel?

Mandrel es la opción recomendada cuando:

  • Trabajas con Red Hat Build of Quarkus (RHBQ)
  • Despliegas en OpenShift o contenedores Linux
  • Necesitas soporte de Red Hat
  • Priorizas estabilidad y trazabilidad
  • Requieres cumplimiento de normas corporativas

ℹ️ Mandrel también incluye SubstrateVM, pero con ajustes y configuraciones adaptadas específicamente a los requisitos de Quarkus.


¿Qué sucede al compilar en modo nativo con Quarkus?

La compilación de aplicaciones Java como ejecutables nativos no es portable de la misma forma que un JAR tradicional.

El binario generado queda ligado al sistema operativo y a la arquitectura donde se realiza la compilación, ya que el proceso produce código máquina específico.

Por ejemplo:

  • Si compilas en macOS ARM, el ejecutable solo funcionará en macOS ARM
  • Si el objetivo es ejecutar en Linux x86_64, la compilación debe realizarse en un entorno compatible con Linux x86_64.

Esta restricción no es una limitación de Quarkus, sino una consecuencia directa de la compilación Ahead-of-Time utilizada por native-image.

La solución de Quarkus

Quarkus aborda este problema desplazando la compilación fuera del entorno del desarrollador. En lugar de depender del sistema operativo local, realiza la compilación nativa dentro de un contenedor Linux que actúa como entorno de destino controlado.

De esta forma, es posible generar ejecutables Linux desde macOS o Windows de manera reproducible y consistente. El binario resultante seguirá siendo específico de Linux, pero el proceso queda desacoplado del sistema anfitrión y se integra fácilmente con pipelines de CI/CD o entornos de construcción estandarizados.

Comandos para compilar en nativo (Windows, Mac OS, Linux)

Con Maven:

./mvnw package -Pnative

Con Quarkus CLI:

quarkus build --native

⚠️ La compilación nativa puede consumir bastante memoria (recomendado ≥ 4–8 GB RAM libre y CPU multi-core). En máquinas con recursos limitados, considera usar un contenedor o compilar en CI/CD.

El proceso paso a paso

  1. Quarkus delega la tarea a native-image
  2. native-image analiza tu código y dependencias
  3. Realiza compilación AOT (Ahead-of-Time — antes de ejecutar)
  4. Integra SubstrateVM en el ejecutable
  5. Aplica configuraciones de reflexión y recursos
  6. Genera el binario listo para ejecutarse

ℹ️ Si usas Red Hat Build of Quarkus, el proceso automáticamente emplea Mandrel como motor de compilación nativa.

Compilación en contenedor (Linux)

Para generar un ejecutable Linux desde cualquier sistema operativo, ejecuta:

./mvnw package -Pnative -Dquarkus.native.container-build=true

Esto descarga una imagen con Mandrel y compila dentro del contenedor, garantizando compatibilidad con Linux. La compilación nativa en contenedor requiere un motor de contenedores corriendo, como Docker o Podman. Quarkus es agnóstico al runtime y utiliza el motor disponible para ejecutar la imagen de compilación nativa.


GraalVM vs Mandrel: tabla comparativa

Ambas distribuciones comparten la capacidad de generar ejecutables nativos con SubstrateVM, pero sus objetivos son distintos:

CriterioGraalVM CommunityMandrel
Caso de uso principalExploración, múltiples lenguajesQuarkus en producción
Tamaño de descargaMayor (incluye más componentes)Menor (solo lo esencial)
SoporteOracle (versión Enterprise)Red Hat (con RHBQ)
ActualizacionesCiclo de OracleAlineado con RHBQ
Compatibilidad OpenShiftFuncionaOptimizado y certificado
Ideal paraDesarrollo, experimentaciónProducción empresarial

¿Cuál elegir?

EscenarioRecomendación
Estoy aprendiendo QuarkusCualquiera de las dos
Proyecto personal o startupGraalVM Community
Empresa con soporte Red HatMandrel + RHBQ
Despliegue en OpenShiftMandrel

🚀 Una nave, múltiples motores

Una aplicación Quarkus puede parecer sencilla, pero cuando se transforma en ejecutable nativo, se convierte en una nave de alto rendimiento ensamblada con precisión.

Comprender esta arquitectura no solo es fascinante: también es clave para tomar decisiones acertadas sobre compilación, rendimiento y despliegue en producción.

Para conocer los casos de uso de los ejecutables nativos, te recomiendo leer el artículo Jar vs ejecutables nativos.