{"schemaVersion":1,"id":"https://lucioduran.com/es/blog/graalvm-native-image-spring-boot-startup","url":"https://lucioduran.com/es/blog/graalvm-native-image-spring-boot-startup","title":"GraalVM Native Image: De 8 Segundos de Startup en Spring Boot a 50ms","description":"Compilación AOT para Spring Boot, desafíos de configuración de reflection, análisis del memory footprint y trade-offs de producción tras seis meses de experiencia operativa.","language":"es","datePublished":"2025-10-16","dateModified":null,"contentFormat":"text/markdown","content":"JVM\n\n# GraalVM Native Image: De 8 Segundos de Startup en Spring Boot a 50ms\n\n2025-10-165 min lectura\n\nLucio Durán\nEngineering Manager & AI Solutions Architect\n\nTambién disponible en: [English](<https://lucioduran.com/blog/graalvm-native-image-spring-boot-startup>)\n\n## El pipeline de compilación AOT\n\n[GraalVM](<https://www.graalvm.org/>) Native Image hace compilación ahead-of-time, convirtiendo tu bytecode Java en un ejecutable nativo standalone. A diferencia de la JVM, que interpreta bytecode y compila JIT los métodos hot en runtime, native image hace toda la compilación en build time a través de un proceso llamado análisis estático.\n\n```bash\n# Build básico de native image para una app Spring Boot\n./mvnw -Pnative native:compile\n```\n\nSpring Boot 3.x incluye soporte de native image via Spring AOT (Ahead-of-Time) processing. Durante el build, Spring AOT analiza tu application context y genera hints de reflection, registraciones de proxy, metadata de serialización y patrones de registro de recursos.\n\n```java\n// build.gradle.kts\nplugins {\n id(\"org.graalvm.buildtools.native\") version \"0.10.4\"\n}\n\ngraalvmNative {\n binaries {\n named(\"main\") {\n imageName.set(\"auth-service\")\n mainClass.set(\"com.example.AuthServiceApplication\")\n buildArgs.add(\"-O2\")\n buildArgs.add(\"-march=native\")\n buildArgs.add(\"--gc=G1\")\n buildArgs.add(\"-H:+ReportExceptionStackTraces\")\n }\n }\n}\n```\n\n## La pesadilla de configuración de reflection\n\nAcá es donde empieza la diversión. La API de reflection de Java, ` Class.forName() `, ` Method.invoke() `, ` Field.get() `, está en todos lados. Spring la usa para inyección de dependencias, Jackson para serialización JSON, Hibernate para mapping de entidades. El análisis estático de native image no puede ver a través de reflection, así que cada acceso reflexivo debe declararse por adelantado.\n\nEl ` native-image-agent ` es tu mejor amigo. Es un agente JVM que registra todos los accese es reflexivos, llamadas JNI, creación de proxies y carga de recurse es durante runtime:\n\n```bash\n# Ejecutar tu app con el tracing agent\njava -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image \\\n -jar target/auth-service.jar\n\n# Ahora ejercitá cada code path, ejecutar tu suite completa de tests\n./run-integration-tests.sh\n```\n\nEl ` reflect-config.json ` generado se ve así:\n\n```json\n[\n {\n \"name\": \"com.example.model.User\",\n \"allDeclaredConstructors\": true,\n \"allPublicMethods\": true,\n \"allDeclaredFields\": true\n },\n {\n \"name\": \"com.fasterxml.jackson.databind.ser.std.ToStringSerializer\",\n \"methods\": [\n { \"name\": \"<init>\", \"parameterTypes\": [] }\n ]\n }\n]\n```\n\nEl problema: el agente solo registra code paths que realmente se ejecutan durante el tracing. Si se omite un code path, aparece un ` NoSuchMethodException ` en producción. Un ejemplo común: un endpoint de refresh de tokens, que solo se dispara cuando los tokens están cerca de expirar, que tira un error de reflection dos semanas después del deploy.\n\nUn approach robusto: correr el agente contra tres escenarios diferentes:\n\n```bash\n# 1. Suite completa de tests de integración\njava -agentlib:native-image-agent=config-merge-dir=native-config \\\n -jar app.jar &\n./run-integration-tests.sh\nkill %1\n\n# 2. Smoke test manual de paths poco testeados\njava -agentlib:native-image-agent=config-merge-dir=native-config \\\n -jar app.jar &\ncurl -X POST localhost:8080/auth/refresh -H \"Authorization: Bearer expired_token\"\ncurl -X POST localhost:8080/auth/logout\nkill %1\n\n# 3. Paths de error, disparar errores de validación, 404s, auth failures\njava -agentlib:native-image-agent=config-merge-dir=native-config \\\n -jar app.jar &\ncurl -X POST localhost:8080/auth/login -d '{\"bad\":\"json\"}'\ncurl -X GET localhost:8080/nonexistent\nkill %1\n```\n\nEl flag ` config-merge-dir ` es crítico, combina entradas nuevas con config existente en vez de sobreescribir.\n\n## Tiempo de startup: el antes y después\n\nLos números reales de nuestro auth-service:\n\n```\nJVM (OpenJDK 21, settings por defecto):\n Startup: 8.247ms\n Tiempo a primer request: 9.100ms\n RSS en idle: 287MB\n RSS bajo carga: 412MB\n\nNative Image (GraalVM 21, -O2, G1 GC):\n Startup: 47ms\n Tiempo a primer request: 52ms\n RSS en idle: 43MB\n RSS bajo carga: 198MB\n```\n\nEso es una mejora de 175x en tiempo de startup. Pero observar algo: bajo carga, la brecha de memoria se achica de 6.7x a 2.1x. La ventaja de native image es principalmente en escenarios de idle/startup.\n\nBenchmarks de throughput para el mismo servicio manejando validación de JWT:\n\n```\nJVM (después de warmup): 47.200 req/s a p99=2.1ms\nNative Image: 31.400 req/s a p99=3.8ms\n```\n\nLa JVM es 50% más rápida en throughput pico. Este es el trade-off fundamental: native image te da startup rápido y poca memoria en idle a costa de throughput pico. Para nuestro servicio de auth (50 req/min), la diferencia de throughput es irrelevante. Para un API gateway de alto tráfico, podría importar.\n\n## Tiempo de build e impacto en CI/CD\n\nLa compilación de native image es brutalmente intensiva en recursos:\n\n```\nBuild JVM (mvn package): 14 segundos, 512MB RAM\nBuild native image (-O2): 11 minutos 23 segundos, 14.2GB RAM\nBuild native image (-Ob): 4 minutos 8 segundos, 8.1GB RAM\n```\n\nLos CI runners necesitan máquinas potentes. Instancias ` c6a.2xlarge ` (8 vCPU, 16GB RAM) son un baseline razonable para builds nativos.\n\nNuestro Dockerfile multi-stage:\n\n```dockerfile\nFROM ghcr.io/graalvm/native-image-community:21 AS builder\nWORKDIR /app\nCOPY . .\nRUN ./mvnw -Pnative native:compile -DskipTests \\\n -Dspring.native.build.args=\"-O2 --gc=G1 -march=x86-64-v3\"\n\nFROM debian:bookworm-slim\nRUN apt-get update && apt-get install -y --no-install-recommends libz-dev \\\n && rm -rf /var/lib/apt/lists/*\nCOPY --from=builder /app/target/auth-service /app/auth-service\nEXPOSE 8080\nENTRYPOINT [\"/app/auth-service\"]\n```\n\nLa imagen final pesa 82MB, comparada con 340MB de la imagen JVM con JDK. En un cluster Kubernetes corriendo 40 instancias, son 10GB menos para descargar en deploys.\n\n## Lecciones de producción\n\nDespués de seis meses de experiencia en producción, las conclusiones clave para equipos considerando GraalVM Native Image:\n\n**Utilizarlo para**: herramientas CLI, funciones serverless, batch jobs de corta vida, microservicios de bajo tráfico, contenedores sidecar, y cualquier workload donde el startup time o la memoria en idle importe más que el throughput pico.\n\n**Evitarlo para**: servicios de alto throughput donde la optimización JIT importa, apps con uso pesado de carga dinámica de clases (plugins, OSGI), servicios que dependen de librerías sin soporte de native image.\n\n**El problema de reflection no desaparece**: Incluso con Spring AOT, las librerías de terceros te sorprenden. Presupuestar tiempo para corridas del tracing agent después de cada upgrade de dependencias.\n\n**Monitoreo**: Los MBeans de JMX de los que dependés para monitoreo JVM no existen en native image. VisualVM no conecta. Necesitás instrumentar tu aplicación diferente, Micrometer con Prometheus funciona, pero Java Flight Recorder no.\n\nUn servicio de auth corriendo en native images durante seis meses demuestra los beneficios claramente. Deploys que tardaban 45 segundos ahora se completan en menos de 3 segundos. Los contenedores bajan de 512MB a 256MB con margen. La diferencia de throughput es invisible a niveles de tráfico típicos. Para este tipo de workload, native image es una victoria indiscutible. Para otros, la respuesta es más matizada, y ese matiz es justamente el punto de este artículo.\n\n## Preguntas frecuentes\n\n### ¿GraalVM Native Image está listo para producción con Spring Boot?\n\nSí, Spring Boot 3.x tiene soporte de primera clase para native image via Spring AOT. La mayoría de los módulos de Spring funcionan out of the box, aunque algunas librerías de terceros requieren configuración manual de reflection.\n\n### ¿Cuáles son los principales trade-offs de la compilación nativa?\n\nStartup más rápido y menor memoria a costa de menor throughput pico (sin optimización JIT), tiempos de build más largos, y uso restringido de reflection/carga dinámica de clases.\n\n### ¿Cuánta memoria usa una app nativa de Spring Boot?\n\nUna REST API típica arranca en 30-60MB RSS comparado con 150-300MB para la misma app en la JVM. Bajo carga, la brecha se achica porque la native image aloca heap de forma similar a la JVM.\n\n## Tips\n\n1. **Tip:** Ejecutar el reporte de ` native-image --initialize-at-build-time ` con ` -H:+PrintClassInitialization ` antes de tu primer build. Te muestra exactamente qué clases están retrasando el startup.\n2. **Tip:** Nunca distribuyas una native image sin correr el ` native-image-agent ` contra tu suite completa de tests de integración. Configs de reflection faltantes solo aparecen en runtime como ` NoSuchMethodException ` en producción.\n3. **Tip:** Utilizar ` -Ob ` (quick build mode) durante desarrollo y ` -O2 ` solo para builds de release. La optimización completa tarda 10-15 minutos y 14GB+ de RAM.\n4. **Tip:** Monitorear memoria RSS, no heap. Las native images usan memoria off-heap para datos image-mapped, code cache y thread stacks. Una app de 128MB de heap puede consumir 350MB RSS en producción.\n\n- graalvm\n- native-image\n- spring-boot\n- aot\n- jvm\n- startup-time\n- performance\n- java\n\n[Volver al Blog](<https://lucioduran.com/es/blog>)","headings":[{"level":1,"text":"GraalVM Native Image: De 8 Segundos de Startup en Spring Boot a 50ms","url":"https://lucioduran.com/es/blog/graalvm-native-image-spring-boot-startup#title"},{"level":2,"text":"El pipeline de compilación AOT","url":"https://lucioduran.com/es/blog/graalvm-native-image-spring-boot-startup#el-pipeline-de-compilacin-aot"},{"level":2,"text":"La pesadilla de configuración de reflection","url":"https://lucioduran.com/es/blog/graalvm-native-image-spring-boot-startup#la-pesadilla-de-configuracin-de-reflection"},{"level":2,"text":"Tiempo de startup: el antes y después","url":"https://lucioduran.com/es/blog/graalvm-native-image-spring-boot-startup#tiempo-de-startup-el-antes-y-despus"},{"level":2,"text":"Tiempo de build e impacto en CI/CD","url":"https://lucioduran.com/es/blog/graalvm-native-image-spring-boot-startup#tiempo-de-build-e-impacto-en-cicd"},{"level":2,"text":"Lecciones de producción","url":"https://lucioduran.com/es/blog/graalvm-native-image-spring-boot-startup#lecciones-de-produccin"},{"level":2,"text":"Preguntas frecuentes","url":"https://lucioduran.com/es/blog/graalvm-native-image-spring-boot-startup#questions"},{"level":3,"text":"¿GraalVM Native Image está listo para producción con Spring Boot?","url":"https://lucioduran.com/es/blog/graalvm-native-image-spring-boot-startup#question-1"},{"level":3,"text":"¿Cuáles son los principales trade-offs de la compilación nativa?","url":"https://lucioduran.com/es/blog/graalvm-native-image-spring-boot-startup#question-2"},{"level":3,"text":"¿Cuánta memoria usa una app nativa de Spring Boot?","url":"https://lucioduran.com/es/blog/graalvm-native-image-spring-boot-startup#question-3"},{"level":2,"text":"Tips","url":"https://lucioduran.com/es/blog/graalvm-native-image-spring-boot-startup#article-tips"}],"sections":[{"heading":"GraalVM Native Image: De 8 Segundos de Startup en Spring Boot a 50ms","level":1,"url":"https://lucioduran.com/es/blog/graalvm-native-image-spring-boot-startup","content":"JVM"},{"heading":"GraalVM Native Image: De 8 Segundos de Startup en Spring Boot a 50ms","level":1,"url":"https://lucioduran.com/es/blog/graalvm-native-image-spring-boot-startup#title","content":"2025-10-165 min lectura\n\nLucio Durán\nEngineering Manager & AI Solutions Architect\n\nTambién disponible en: [English](<https://lucioduran.com/blog/graalvm-native-image-spring-boot-startup>)"},{"heading":"El pipeline de compilación AOT","level":2,"url":"https://lucioduran.com/es/blog/graalvm-native-image-spring-boot-startup#el-pipeline-de-compilacin-aot","content":"[GraalVM](<https://www.graalvm.org/>) Native Image hace compilación ahead-of-time, convirtiendo tu bytecode Java en un ejecutable nativo standalone. A diferencia de la JVM, que interpreta bytecode y compila JIT los métodos hot en runtime, native image hace toda la compilación en build time a través de un proceso llamado análisis estático.\n\n```bash\n# Build básico de native image para una app Spring Boot\n./mvnw -Pnative native:compile\n```\n\nSpring Boot 3.x incluye soporte de native image via Spring AOT (Ahead-of-Time) processing. Durante el build, Spring AOT analiza tu application context y genera hints de reflection, registraciones de proxy, metadata de serialización y patrones de registro de recursos.\n\n```java\n// build.gradle.kts\nplugins {\n id(\"org.graalvm.buildtools.native\") version \"0.10.4\"\n}\n\ngraalvmNative {\n binaries {\n named(\"main\") {\n imageName.set(\"auth-service\")\n mainClass.set(\"com.example.AuthServiceApplication\")\n buildArgs.add(\"-O2\")\n buildArgs.add(\"-march=native\")\n buildArgs.add(\"--gc=G1\")\n buildArgs.add(\"-H:+ReportExceptionStackTraces\")\n }\n }\n}\n```"},{"heading":"La pesadilla de configuración de reflection","level":2,"url":"https://lucioduran.com/es/blog/graalvm-native-image-spring-boot-startup#la-pesadilla-de-configuracin-de-reflection","content":"Acá es donde empieza la diversión. La API de reflection de Java, ` Class.forName() `, ` Method.invoke() `, ` Field.get() `, está en todos lados. Spring la usa para inyección de dependencias, Jackson para serialización JSON, Hibernate para mapping de entidades. El análisis estático de native image no puede ver a través de reflection, así que cada acceso reflexivo debe declararse por adelantado.\n\nEl ` native-image-agent ` es tu mejor amigo. Es un agente JVM que registra todos los accese es reflexivos, llamadas JNI, creación de proxies y carga de recurse es durante runtime:\n\n```bash\n# Ejecutar tu app con el tracing agent\njava -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image \\\n -jar target/auth-service.jar\n\n# Ahora ejercitá cada code path, ejecutar tu suite completa de tests\n./run-integration-tests.sh\n```\n\nEl ` reflect-config.json ` generado se ve así:\n\n```json\n[\n {\n \"name\": \"com.example.model.User\",\n \"allDeclaredConstructors\": true,\n \"allPublicMethods\": true,\n \"allDeclaredFields\": true\n },\n {\n \"name\": \"com.fasterxml.jackson.databind.ser.std.ToStringSerializer\",\n \"methods\": [\n { \"name\": \"<init>\", \"parameterTypes\": [] }\n ]\n }\n]\n```\n\nEl problema: el agente solo registra code paths que realmente se ejecutan durante el tracing. Si se omite un code path, aparece un ` NoSuchMethodException ` en producción. Un ejemplo común: un endpoint de refresh de tokens, que solo se dispara cuando los tokens están cerca de expirar, que tira un error de reflection dos semanas después del deploy.\n\nUn approach robusto: correr el agente contra tres escenarios diferentes:\n\n```bash\n# 1. Suite completa de tests de integración\njava -agentlib:native-image-agent=config-merge-dir=native-config \\\n -jar app.jar &\n./run-integration-tests.sh\nkill %1\n\n# 2. Smoke test manual de paths poco testeados\njava -agentlib:native-image-agent=config-merge-dir=native-config \\\n -jar app.jar &\ncurl -X POST localhost:8080/auth/refresh -H \"Authorization: Bearer expired_token\"\ncurl -X POST localhost:8080/auth/logout\nkill %1\n\n# 3. Paths de error, disparar errores de validación, 404s, auth failures\njava -agentlib:native-image-agent=config-merge-dir=native-config \\\n -jar app.jar &\ncurl -X POST localhost:8080/auth/login -d '{\"bad\":\"json\"}'\ncurl -X GET localhost:8080/nonexistent\nkill %1\n```\n\nEl flag ` config-merge-dir ` es crítico, combina entradas nuevas con config existente en vez de sobreescribir."},{"heading":"Tiempo de startup: el antes y después","level":2,"url":"https://lucioduran.com/es/blog/graalvm-native-image-spring-boot-startup#tiempo-de-startup-el-antes-y-despus","content":"Los números reales de nuestro auth-service:\n\n```\nJVM (OpenJDK 21, settings por defecto):\n Startup: 8.247ms\n Tiempo a primer request: 9.100ms\n RSS en idle: 287MB\n RSS bajo carga: 412MB\n\nNative Image (GraalVM 21, -O2, G1 GC):\n Startup: 47ms\n Tiempo a primer request: 52ms\n RSS en idle: 43MB\n RSS bajo carga: 198MB\n```\n\nEso es una mejora de 175x en tiempo de startup. Pero observar algo: bajo carga, la brecha de memoria se achica de 6.7x a 2.1x. La ventaja de native image es principalmente en escenarios de idle/startup.\n\nBenchmarks de throughput para el mismo servicio manejando validación de JWT:\n\n```\nJVM (después de warmup): 47.200 req/s a p99=2.1ms\nNative Image: 31.400 req/s a p99=3.8ms\n```\n\nLa JVM es 50% más rápida en throughput pico. Este es el trade-off fundamental: native image te da startup rápido y poca memoria en idle a costa de throughput pico. Para nuestro servicio de auth (50 req/min), la diferencia de throughput es irrelevante. Para un API gateway de alto tráfico, podría importar."},{"heading":"Tiempo de build e impacto en CI/CD","level":2,"url":"https://lucioduran.com/es/blog/graalvm-native-image-spring-boot-startup#tiempo-de-build-e-impacto-en-cicd","content":"La compilación de native image es brutalmente intensiva en recursos:\n\n```\nBuild JVM (mvn package): 14 segundos, 512MB RAM\nBuild native image (-O2): 11 minutos 23 segundos, 14.2GB RAM\nBuild native image (-Ob): 4 minutos 8 segundos, 8.1GB RAM\n```\n\nLos CI runners necesitan máquinas potentes. Instancias ` c6a.2xlarge ` (8 vCPU, 16GB RAM) son un baseline razonable para builds nativos.\n\nNuestro Dockerfile multi-stage:\n\n```dockerfile\nFROM ghcr.io/graalvm/native-image-community:21 AS builder\nWORKDIR /app\nCOPY . .\nRUN ./mvnw -Pnative native:compile -DskipTests \\\n -Dspring.native.build.args=\"-O2 --gc=G1 -march=x86-64-v3\"\n\nFROM debian:bookworm-slim\nRUN apt-get update && apt-get install -y --no-install-recommends libz-dev \\\n && rm -rf /var/lib/apt/lists/*\nCOPY --from=builder /app/target/auth-service /app/auth-service\nEXPOSE 8080\nENTRYPOINT [\"/app/auth-service\"]\n```\n\nLa imagen final pesa 82MB, comparada con 340MB de la imagen JVM con JDK. En un cluster Kubernetes corriendo 40 instancias, son 10GB menos para descargar en deploys."},{"heading":"Lecciones de producción","level":2,"url":"https://lucioduran.com/es/blog/graalvm-native-image-spring-boot-startup#lecciones-de-produccin","content":"Después de seis meses de experiencia en producción, las conclusiones clave para equipos considerando GraalVM Native Image:\n\n**Utilizarlo para**: herramientas CLI, funciones serverless, batch jobs de corta vida, microservicios de bajo tráfico, contenedores sidecar, y cualquier workload donde el startup time o la memoria en idle importe más que el throughput pico.\n\n**Evitarlo para**: servicios de alto throughput donde la optimización JIT importa, apps con uso pesado de carga dinámica de clases (plugins, OSGI), servicios que dependen de librerías sin soporte de native image.\n\n**El problema de reflection no desaparece**: Incluso con Spring AOT, las librerías de terceros te sorprenden. Presupuestar tiempo para corridas del tracing agent después de cada upgrade de dependencias.\n\n**Monitoreo**: Los MBeans de JMX de los que dependés para monitoreo JVM no existen en native image. VisualVM no conecta. Necesitás instrumentar tu aplicación diferente, Micrometer con Prometheus funciona, pero Java Flight Recorder no.\n\nUn servicio de auth corriendo en native images durante seis meses demuestra los beneficios claramente. Deploys que tardaban 45 segundos ahora se completan en menos de 3 segundos. Los contenedores bajan de 512MB a 256MB con margen. La diferencia de throughput es invisible a niveles de tráfico típicos. Para este tipo de workload, native image es una victoria indiscutible. Para otros, la respuesta es más matizada, y ese matiz es justamente el punto de este artículo."},{"heading":"Preguntas frecuentes","level":2,"url":"https://lucioduran.com/es/blog/graalvm-native-image-spring-boot-startup#questions","content":""},{"heading":"¿GraalVM Native Image está listo para producción con Spring Boot?","level":3,"url":"https://lucioduran.com/es/blog/graalvm-native-image-spring-boot-startup#question-1","content":"Sí, Spring Boot 3.x tiene soporte de primera clase para native image via Spring AOT. La mayoría de los módulos de Spring funcionan out of the box, aunque algunas librerías de terceros requieren configuración manual de reflection."},{"heading":"¿Cuáles son los principales trade-offs de la compilación nativa?","level":3,"url":"https://lucioduran.com/es/blog/graalvm-native-image-spring-boot-startup#question-2","content":"Startup más rápido y menor memoria a costa de menor throughput pico (sin optimización JIT), tiempos de build más largos, y uso restringido de reflection/carga dinámica de clases."},{"heading":"¿Cuánta memoria usa una app nativa de Spring Boot?","level":3,"url":"https://lucioduran.com/es/blog/graalvm-native-image-spring-boot-startup#question-3","content":"Una REST API típica arranca en 30-60MB RSS comparado con 150-300MB para la misma app en la JVM. Bajo carga, la brecha se achica porque la native image aloca heap de forma similar a la JVM."},{"heading":"Tips","level":2,"url":"https://lucioduran.com/es/blog/graalvm-native-image-spring-boot-startup#article-tips","content":"1. **Tip:** Ejecutar el reporte de ` native-image --initialize-at-build-time ` con ` -H:+PrintClassInitialization ` antes de tu primer build. Te muestra exactamente qué clases están retrasando el startup.\n2. **Tip:** Nunca distribuyas una native image sin correr el ` native-image-agent ` contra tu suite completa de tests de integración. Configs de reflection faltantes solo aparecen en runtime como ` NoSuchMethodException ` en producción.\n3. **Tip:** Utilizar ` -Ob ` (quick build mode) durante desarrollo y ` -O2 ` solo para builds de release. La optimización completa tarda 10-15 minutos y 14GB+ de RAM.\n4. **Tip:** Monitorear memoria RSS, no heap. Las native images usan memoria off-heap para datos image-mapped, code cache y thread stacks. Una app de 128MB de heap puede consumir 350MB RSS en producción.\n\n- graalvm\n- native-image\n- spring-boot\n- aot\n- jvm\n- startup-time\n- performance\n- java\n\n[Volver al Blog](<https://lucioduran.com/es/blog>)"}],"links":[{"text":"English","url":"https://lucioduran.com/blog/graalvm-native-image-spring-boot-startup"},{"text":"GraalVM","url":"https://www.graalvm.org/"},{"text":"Volver al Blog","url":"https://lucioduran.com/es/blog"}],"structuredData":[{"@context":"https://schema.org","@graph":[{"@type":"BlogPosting","@id":"https://lucioduran.com/es/blog/graalvm-native-image-spring-boot-startup#article","mainEntityOfPage":{"@type":"WebPage","@id":"https://lucioduran.com/es/blog/graalvm-native-image-spring-boot-startup"},"headline":"GraalVM Native Image: De 8 Segundos de Startup en Spring Boot a 50ms","description":"Compilación AOT para Spring Boot, desafíos de configuración de reflection, análisis del memory footprint y trade-offs de producción tras seis meses de experiencia operativa.","datePublished":"2025-10-16","author":{"@type":"Person","@id":"https://lucioduran.com/#person","name":"Lucio Durán","url":"https://lucioduran.com"},"publisher":{"@type":"Person","@id":"https://lucioduran.com/#person","name":"Lucio Durán","url":"https://lucioduran.com"},"image":"https://statics.lucioduran.com/images/lucioduran-social-card.jpg","url":"https://lucioduran.com/es/blog/graalvm-native-image-spring-boot-startup","inLanguage":"es","isPartOf":{"@id":"https://lucioduran.com/blog#blog"},"articleSection":"JVM","keywords":"graalvm, native-image, spring-boot, aot, jvm, startup-time, performance, java","wordCount":969,"timeRequired":"PT5M","workTranslation":[{"@type":"BlogPosting","inLanguage":"en","url":"https://lucioduran.com/blog/graalvm-native-image-spring-boot-startup"}]},{"@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Inicio","item":"https://lucioduran.com/"},{"@type":"ListItem","position":2,"name":"Blog","item":"https://lucioduran.com/es/blog"},{"@type":"ListItem","position":3,"name":"GraalVM Native Image: De 8 Segundos de Startup en Spring Boot a 50ms","item":"https://lucioduran.com/es/blog/graalvm-native-image-spring-boot-startup"}]},{"@type":"FAQPage","mainEntity":[{"@type":"Question","name":"¿GraalVM Native Image está listo para producción con Spring Boot?","acceptedAnswer":{"@type":"Answer","text":"Sí, Spring Boot 3.x tiene soporte de primera clase para native image via Spring AOT. La mayoría de los módulos de Spring funcionan out of the box, aunque algunas librerías de terceros requieren configuración manual de reflection."}},{"@type":"Question","name":"¿Cuáles son los principales trade-offs de la compilación nativa?","acceptedAnswer":{"@type":"Answer","text":"Startup más rápido y menor memoria a costa de menor throughput pico (sin optimización JIT), tiempos de build más largos, y uso restringido de reflection/carga dinámica de clases."}},{"@type":"Question","name":"¿Cuánta memoria usa una app nativa de Spring Boot?","acceptedAnswer":{"@type":"Answer","text":"Una REST API típica arranca en 30-60MB RSS comparado con 150-300MB para la misma app en la JVM. Bajo carga, la brecha se achica porque la native image aloca heap de forma similar a la JVM."}}]}]}],"contentHash":"d88789dfe1a3084f75097d0d616b3f216b3b80309a8954764092aa3cf3790d5c","provenance":{"method":"Deterministic serialization of the server-rendered main element","source":"https://lucioduran.com/es/blog/graalvm-native-image-spring-boot-startup","hashAlgorithm":"sha256","hashScope":"JSON document fields before contentHash and provenance","sourceHTMLBytes":56777,"contentBytes":9349}}
