GraalVM Native & AOT - Spring Boot application’ni odatdagi JVM’da ishga tushirishdan tashqari, oldindan kompilyatsiya qilingan native executable ko‘rinishida yuritish mavzusi.
Bu blok quyidagilarni o‘z ichiga oladi: Spring Native / GraalVM native image, Spring 6 AOT compilation, reflection hints & proxy hints, native image trade-offlari, va qachon native, qachon JVM tanlash.
1. Muammo nimada?
Oddiy Spring Boot application odatda JVM’da ishlaydi:
Java code
↓
Bytecode
↓
JVM
↓
Runtime classpath scanning
↓
Spring context startup
↓
Application readySpring Boot application start vaqtida ko‘p ish qiladi:
classpath scan
annotation scan
bean definition yaratish
auto-configuration tanlash
reflection ishlatish
proxy yaratish
configuration binding
condition tekshirishKatta service’da bu startup vaqtini oshiradi.
Masalan:
order-service JVM startup: 5-20 sekund
serverless function cold start: juda sekin
Kubernetes scale-up: pod tez ready bo‘lmaydiShu yerda AOT va Native Image yordam beradi.
2. AOT nima?
AOT - Ahead-of-Time processing.
Ya’ni Spring runtime’da qiladigan ayrim ishlarni build vaqtida oldindan bajaradi.
Spring Framework hujjatlarida AOT optimizations ApplicationContextni build vaqtida tekshirib, odatda runtime’da qilinadigan discovery va decision logic’ni oldindan bajarishi aytilgan. Bu startup arrangement’ni soddaroq va aniqroq qiladi. (Home)
Oddiy JVM mode:
Application start bo‘ldi
↓
Spring classpath scan qildi
↓
Beanlarni topdi
↓
Proxy yaratdi
↓
Reflection metadata ishlatdiAOT mode:
Build vaqtida Spring analiz qiladi
↓
Bean definitions oldindan tayyorlanadi
↓
Reflection/proxy/resource hints generatsiya qilinadi
↓
Startup paytida kamroq ish qoladi3. GraalVM Native Image nima?
GraalVM Native Image Java application’ni JVM bytecode sifatida emas, balki native executable qilib build qiladi.
Ya’ni:
Spring Boot app
↓
AOT processing
↓
GraalVM native-image
↓
Linux/macOS/Windows executableNatija:
./order-serviceUni ishga tushirish uchun alohida JVM o‘rnatish shart emas.
JVM vs Native Image
Mezon | JVM mode | Native Image |
|---|---|---|
Startup | Sekinroq | Juda tez |
Memory footprint | Ko‘proq bo‘lishi mumkin | Ko‘pincha kamroq |
Peak throughput | Ko‘pincha kuchliroq | Ba’zida pastroq |
Build time | Tezroq | Ancha sekinroq |
Debug qilish | Osonroq | Qiyinroq |
Reflection | Runtime’da erkinroq | Oldindan hint kerak |
Dynamic class loading | Moslashuvchan | Cheklangan |
Serverless | Har doim ham ideal emas | Juda mos |
Long-running service | Juda yaxshi | Vaziyatga qarab |
4. Spring 6 AOT qanday ishlaydi?
Spring 6 va Spring Boot 3 native image’ni qo‘llash uchun AOT engine bilan keladi.
Build vaqtida Spring quyidagilarni qiladi:
@Configuration classlarni analiz qiladi
@Bean metodlarni ko‘radi
@Component scan natijalarini oldindan hisoblaydi
conditional beanlarni tekshiradi
proxy kerak joylarni aniqlaydi
reflection kerak classlarni ro‘yxatga oladi
resource kerak bo‘lsa qo‘shadi
generated source/code yaratadiBu native image uchun muhim, chunki GraalVM build vaqtida iloji boricha hamma narsani bilishi kerak.
5. Native Image nima uchun reflection’dan qo‘rqadi?
Java’da reflection odatda runtime’da ishlaydi:
Class<?> clazz = Class.forName("com.company.Order");
Object obj = clazz.getDeclaredConstructor().newInstance();JVM buni runtime’da qila oladi.
Lekin native image build qilinayotganda GraalVM shunday savol beradi:
Bu class runtime’da reflection orqali ishlatiladimi?
Constructor kerakmi?
Field kerakmi?
Method kerakmi?
Annotation kerakmi?Agar oldindan aytilmasa, runtime’da xato chiqishi mumkin.
6. Runtime Hints
Spring’da native image uchun reflection, resource, proxy kabi narsalarni oldindan ko‘rsatish kerak bo‘lsa, RuntimeHints ishlatiladi.
Masalan:
public class OrderRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
hints.reflection().registerType(
Order.class,
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
MemberCategory.INVOKE_PUBLIC_METHODS,
MemberCategory.DECLARED_FIELDS
);
}
}Keyin config’da ulaymiz:
@ImportRuntimeHints(OrderRuntimeHints.class)
@Configuration
public class OrderNativeConfiguration {
}Spring Framework hujjatlarida AOT restrictions borligi ko‘rsatiladi: build vaqtidagi optimizatsiya application’ni classpath va environment asosida oldindan belgilangan holatga keltiradi. Shuning uchun runtime’da juda dinamik behavior ishlatish native image’da muammo bo‘lishi mumkin. (Home)
7. Proxy Hints
Spring ko‘p joyda proxy ishlatadi:
@Transactional
@Async
@Cacheable
@Validated
Spring Security method security
AOPJVM’da proxy runtime’da yaratiladi.
Native image’da esa proxy kerakligini oldindan bilish kerak bo‘ladi.
Masalan interface proxy:
hints.proxies().registerJdkProxy(
OrderService.class,
SpringProxy.class,
Advised.class
);Ko‘p oddiy Spring holatlarida Spring Boot AOT bularni avtomatik aniqlaydi. Lekin custom AOP, custom reflection yoki third-party library bo‘lsa, qo‘shimcha hint kerak bo‘lishi mumkin.
8. Resource Hints
Agar application runtime’da fayl o‘qisa:
schema.sql
templates/email.html
certificates/public.pem
mapper-config.jsonnative image ichiga bu resource’larni qo‘shish kerak.
Misol:
public class ResourceHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
hints.resources().registerPattern("templates/*.html");
hints.resources().registerPattern("certificates/*.pem");
}
}Aks holda JVM’da ishlagan narsa native’da topilmasligi mumkin.
9. Native Image build qilish
Maven
Odatda Spring Boot native image uchun native profile ishlatiladi:
./mvnw -Pnative native:compileYoki container image:
./mvnw -Pnative spring-boot:build-imageGradle
./gradlew nativeCompileNatijada executable chiqadi:
target/order-serviceyoki:
build/native/nativeCompile/order-service10. Native application startup
JVM:
java -jar order-service.jarNative:
./order-serviceNative’da startup odatda juda tez bo‘ladi:
JVM: sekundlar
Native: millisekundlar yoki juda kam sekundBu ayniqsa foydali:
serverless functions
CLI tools
short-lived jobs
Kubernetes rapid scale-up
cold start sensitive workloads
edge deployment11. Native Image trade-offlari
Native image faqat "tezroq" degani emas. Bu trade-off.
GraalVM Native Image optimizatsiya va performance hujjatlarida native executable’ni performance, file size, build time va debuggability kabi metrikalar bo‘yicha sozlash mexanizmlari borligi aytiladi. (GraalVM)
Afzalliklar
startup juda tez
memory footprint ko‘pincha pastroq
JVM kerak emas
container image kichikroq bo‘lishi mumkin
serverless cold start yaxshi
Kubernetes scale-up tezroqKamchiliklar
build vaqti uzoq
native build ko‘proq RAM talab qiladi
reflection/proxy/resource muammolari chiqishi mumkin
debug qilish qiyinroq
ba’zi librarylar native’da to‘liq mos emas
peak throughput JVM’dan past bo‘lishi mumkin
dynamic class loading cheklangan12. Startup vs Throughput
Native image ko‘pincha startup bo‘yicha yutadi.
Lekin uzoq ishlaydigan, juda yuqori throughput talab qiladigan service uchun JVM ba’zida yaxshiroq bo‘lishi mumkin.
Sabab:
JVM JIT compiler runtime’da kodni optimallashtiradi
hot pathlarni aniqlaydi
uzoq ishlagan sari performance yaxshilanishi mumkinNative image esa oldindan kompilyatsiya qilingan. Startup tez, lekin JVM JIT’ning uzoq muddatli optimizatsiyalari yo‘q.
Arxitektor xulosa:
Native image - har doim JVM’dan tezroq degani emas. U asosan startup va memory bo‘yicha kuchli.
13. Memory masalasi
GraalVM Native Image memory management hujjatlarida native image uchun turli GC implementatsiyalar mavjudligi, Serial GC default ekanligi va u kichik heap hamda past memory footprint uchun optimallashtirilgani ko‘rsatiladi. (Oracle Docs)
Bu serverless yoki kichik containerlarda foydali.
Lekin katta long-running API uchun faqat "native kam RAM yeydi" deb qaror qilish noto‘g‘ri. Har doim real workload bilan benchmark qilish kerak.
14. Native image’da muammo beradigan joylar
Quyidagi narsalar native’da ehtiyot talab qiladi:
Reflection
Dynamic proxy
CGLIB proxy
Dynamic class loading
Serialization/deserialization
Jackson polymorphic mapping
Hibernate lazy proxy
JPA metamodel
Custom annotation scanning
JNI
File/resource loading
Third-party SDK
Scripting engines
Runtime-generated bytecodeMasalan, Jackson DTO’larni reflection orqali ishlatsa, Spring Boot ko‘p holatda hintlarni avtomatik chiqaradi. Lekin custom serializer/deserializer, polymorphic type yoki external SDK bo‘lsa, qo‘lda hint kerak bo‘lishi mumkin.
15. Spring Boot + JPA + Native
JPA native image’da ishlaydi, lekin oddiy REST API’dan murakkabroq.
Sabab:
Hibernate reflection ishlatadi
Entity proxy bor
Lazy loading bor
Annotation metadata kerakSpring Boot AOT ko‘p narsani hal qiladi. Lekin arxitektor sifatida buni bilish kerak:
Native image’da eng oson workload - oddiy REST, HTTP client, JSON, small service. Eng murakkab workload - reflection-heavy ORM, dynamic library, eski SDK’lar.
16. Qachon GraalVM Native tanlash kerak?
Juda yaxshi mos keladi
Serverless function
CLI application
Short-lived batch job
Small REST microservice
Kubernetes’da tez scale-up kerak bo‘lgan service
Memory limit qattiq bo‘lgan container
Edge service
Internal toolMasalan:
notification-worker
image-resize-function
pdf-generator-cli
webhook-handler
small-auth-adapterEhtiyotkorlik bilan tanlash kerak
katta monolith
og‘ir JPA/Hibernate service
ko‘p reflection ishlatadigan legacy app
ko‘p dynamic plugin ishlatadigan system
yuqori throughput talab qiladigan long-running service
native support’i noaniq third-party SDKBu holatda JVM ko‘pincha xavfsizroq.
17. JVM qachon yaxshiroq?
JVM tanlash yaxshi bo‘ladi:
service doim ishlab tursa
startup critical bo‘lmasa
throughput muhim bo‘lsa
JIT optimizatsiya foydali bo‘lsa
debug/profiling muhim bo‘lsa
library ecosystem murakkab bo‘lsa
team native image tajribasiz bo‘lsaMasalan:
high-traffic payment API
large business monolith
analytics service
complex report generator
legacy integration service18. Native vs JVM qaror jadvali
Savol | Native tarafga | JVM tarafga |
|---|---|---|
Startup juda muhimmi? | Ha | Yo‘q |
Memory limit qattiqmi? | Ha | Yo‘q |
Service qisqa ishlaydimi? | Ha | Yo‘q |
Workload uzoq ishlaydimi? | Yo‘q | Ha |
Reflection ko‘pmi? | Yo‘q | Ha |
Build pipeline kuchlimi? | Ha | Shart emas |
Debug/profiling ko‘p kerakmi? | Yo‘q | Ha |
Serverless bormi? | Ha | Kamroq |
19. Arxitektor qaror formulasi
Agar startup + memory muhim bo‘lsa
va application nisbatan statik bo‘lsa
va librarylar native image’ni qo‘llasa
→ GraalVM Native ko‘rib chiqiladi.
Agar service long-running bo‘lsa
va throughput muhim bo‘lsa
va reflection/dynamic behavior ko‘p bo‘lsa
→ JVM yaxshiroq.20. Spring Native eski nomi haqida
Oldin Spring Native degan alohida project bor edi. Spring Boot 3 va Spring Framework 6 bilan native support asosiy ekotizimga kirib keldi.
Bugungi yondashuv:
Spring Boot 3+
Spring Framework 6+
GraalVM Native Image
AOT processing
Runtime hintsYa’ni yangi projectlarda alohida eski spring-native projectiga tayanish kerak emas.
21. Production’da tekshirish checklist
Native image’ni production’ga chiqarishdan oldin quyidagilar tekshiriladi:
Application native build bo‘ladimi?
Barcha endpointlar ishlaydimi?
JSON serialization/deserialization to‘g‘rimi?
Validation ishlaydimi?
Security filter chain ishlaydimi?
JPA queries ishlaydimi?
Migration tool ishlaydimi?
Kafka/Rabbit listener ishlaydimi?
Actuator health/metrics ishlaydimi?
SSL/certificate/resource fayllar topiladimi?
Container image size va startup o‘lchandimi?
Memory usage real load’da tekshirildimi?
Throughput JVM bilan solishtirildimi?Eng muhim qism:
Native image qarori benchmark bilan qilinadi, taxmin bilan emas.
22. Oddiy native-ready coding qoidalari
1. Runtime’da class nomidan object yaratishni kamaytirish
Yomon:
Class<?> clazz = Class.forName(className);
Object instance = clazz.getDeclaredConstructor().newInstance();Yaxshi:
Map<String, PaymentHandler> handlers;2. Reflection ishlatsangiz hint bering
@ImportRuntimeHints(PaymentRuntimeHints.class)
@Configuration
class PaymentNativeConfig {
}3. Resource fayllarni aniq ko‘rsating
hints.resources().registerPattern("templates/*.html");4. Third-party library native support’ini tekshiring
Agar SDK eski bo‘lsa, native image’da muammo ko‘p bo‘lishi mumkin.
5. CI’da native build qo‘shing
Faqat local’da emas, CI’da ham native build ishlashi kerak:
pull request
↓
unit tests
↓
integration tests
↓
nativeCompile
↓
native smoke test23. Real misol: webhook service
Webhook service:
request qabul qiladi
signature tekshiradi
eventni Kafka’ga yozadi
tez javob qaytaradiBu native uchun yaxshi nomzod:
kichik service
startup muhim
memory kam
logic oddiy
reflection kamArchitecture:
webhook-service native image
↓
Kubernetes’da tez start
↓
autoscaling tezroq
↓
cold start kamroq24. Real misol: katta ERP monolith
ERP monolith:
500+ entity
Hibernate complex mapping
dynamic report engine
reflection-heavy library
ko‘p scheduled job
legacy SDKBu native uchun xavfli nomzod.
Yaxshiroq qaror:
JVM’da qoldirish
startup optimizatsiya qilish
lazy initialization tekshirish
container memory tuning
JVM/CDS ko‘rib chiqish25. Eng ko‘p xatolar
Xato 1: Native image’ni silver bullet deb o‘ylash
Native image hamma muammoni hal qilmaydi.
U asosan:
startup
memory
cold startmuammolarini hal qiladi.
Xato 2: Benchmark qilmasdan qaror qilish
To‘g‘ri test:
startup time
RSS memory
heap usage
CPU
p95/p99 latency
throughput
container image size
build timeJVM va native ikkalasi bir xil load’da solishtiriladi.
Xato 3: Library compatibility tekshirmaslik
Agar critical SDK native image’da ishlamasa, production risk katta.
Xato 4: Native build’ni CI’ga qo‘shmaslik
Local’da ishlagan native build keyin dependency update’dan buzilishi mumkin.
Xato 5: Reflection hintlarni tartibsiz yozish
Keraksiz ko‘p hint native image size va attack surface’ni oshirishi mumkin.
26. Arxitektor xulosasi
GraalVM Native & AOT - Spring Boot application’ni tezroq start qiladigan, ko‘pincha kamroq memory ishlatadigan native executable ko‘rinishida chiqarish imkonini beradi.
Lekin arxitektor qarori bunday bo‘ladi:
Native image kerakmi?
Startup haqiqatan muhimmi?
Memory haqiqatan muhimmi?
Build pipeline bunga tayyormi?
Librarylar mosmi?
Team support qila oladimi?
JVM bilan benchmark qildikmi?Eng muhim qoida:
GraalVM Native - performance strategiya emas, deployment va startup strategiya. Uni to‘g‘ri workload uchun ishlatish kerak.
Qisqa tavsiya:
Serverless, CLI, short-lived job, small microservice → Native yaxshi.
Katta long-running, throughput-heavy, reflection-heavy service → JVM ko‘pincha yaxshi.