GraalVM Native & AOT

02.07.2026 | Muallif: Jaxongir a.k.a | Kategoriya: Spring Boot | 17 daqiqa o'qish

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 ready

Spring 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 tekshirish

Katta 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‘lmaydi

Shu 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 ishlatdi

AOT mode:

Build vaqtida Spring analiz qiladi
   ↓
Bean definitions oldindan tayyorlanadi
   ↓
Reflection/proxy/resource hints generatsiya qilinadi
   ↓
Startup paytida kamroq ish qoladi

3. 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 executable

Natija:

./order-service

Uni 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 yaratadi

Bu 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
AOP

JVM’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.json

native 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:compile

Yoki container image:

./mvnw -Pnative spring-boot:build-image

Gradle

./gradlew nativeCompile

Natijada executable chiqadi:

target/order-service

yoki:

build/native/nativeCompile/order-service

10. Native application startup

JVM:

java -jar order-service.jar

Native:

./order-service

Native’da startup odatda juda tez bo‘ladi:

JVM:    sekundlar
Native: millisekundlar yoki juda kam sekund

Bu ayniqsa foydali:

serverless functions
CLI tools
short-lived jobs
Kubernetes rapid scale-up
cold start sensitive workloads
edge deployment

11. 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 tezroq

Kamchiliklar

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 cheklangan

12. 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 mumkin

Native 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 bytecode

Masalan, 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 kerak

Spring 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 tool

Masalan:

notification-worker
image-resize-function
pdf-generator-cli
webhook-handler
small-auth-adapter

Ehtiyotkorlik 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 SDK

Bu 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‘lsa

Masalan:

high-traffic payment API
large business monolith
analytics service
complex report generator
legacy integration service

18. 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 hints

Ya’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 test

23. Real misol: webhook service

Webhook service:

request qabul qiladi
signature tekshiradi
eventni Kafka’ga yozadi
tez javob qaytaradi

Bu native uchun yaxshi nomzod:

kichik service
startup muhim
memory kam
logic oddiy
reflection kam

Architecture:

webhook-service native image
   ↓
Kubernetes’da tez start
   ↓
autoscaling tezroq
   ↓
cold start kamroq

24. Real misol: katta ERP monolith

ERP monolith:

500+ entity
Hibernate complex mapping
dynamic report engine
reflection-heavy library
ko‘p scheduled job
legacy SDK

Bu native uchun xavfli nomzod.

Yaxshiroq qaror:

JVM’da qoldirish
startup optimizatsiya qilish
lazy initialization tekshirish
container memory tuning
JVM/CDS ko‘rib chiqish

25. 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 start

muammolarini 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 time

JVM 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.