<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Java cold start arşivleri - Ali Karahisar</title>
	<atom:link href="https://alikarahisar.com/blog/tag/java-cold-start/feed/" rel="self" type="application/rss+xml" />
	<link>https://alikarahisar.com/blog/tag/java-cold-start/</link>
	<description>Yazılım ve AI Hakkında Karalamalar</description>
	<lastBuildDate>Thu, 03 Sep 2026 11:16:28 +0000</lastBuildDate>
	<language>tr</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1</generator>

<image>
	<url>https://alikarahisar.com/blog/wp-content/uploads/2026/08/cropped-Code_Generated_Image-32x32.png</url>
	<title>Java cold start arşivleri - Ali Karahisar</title>
	<link>https://alikarahisar.com/blog/tag/java-cold-start/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>GraalVM Native Image ile Serverless ve Mikroservislerde Cold Start Sorununu Çözmek</title>
		<link>https://alikarahisar.com/blog/graalvm-native-image-cold-start-spring-boot-3/</link>
					<comments>https://alikarahisar.com/blog/graalvm-native-image-cold-start-spring-boot-3/#respond</comments>
		
		<dc:creator><![CDATA[Ali Karahisar]]></dc:creator>
		<pubDate>Thu, 03 Sep 2026 11:16:22 +0000</pubDate>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Yazılım]]></category>
		<category><![CDATA[AOT derleme]]></category>
		<category><![CDATA[cloud-native Java]]></category>
		<category><![CDATA[GraalVM Native Image]]></category>
		<category><![CDATA[Java cold start]]></category>
		<category><![CDATA[JIT derleme]]></category>
		<category><![CDATA[memory footprint]]></category>
		<category><![CDATA[mikroservis performansı]]></category>
		<category><![CDATA[native executable]]></category>
		<category><![CDATA[serverless Java]]></category>
		<category><![CDATA[Spring AOT]]></category>
		<category><![CDATA[Spring Boot 3 native image]]></category>
		<guid isPermaLink="false">https://alikarahisar.com/blog/?p=44</guid>

					<description><![CDATA[<p>Java; güçlü ekosistemi, olgun araçları ve yüksek çalışma zamanı performansıyla mikroservis geliştirmede en güvenilir seçeneklerden biri. Ancak konu serverless fonksiyonlar, kısa ömürlü container’lar ve hızla ölçeklenen Kubernetes iş yükleri olduğunda klasik JVM modelinin iki maliyeti daha görünür hâle gelir: başlangıç süresi ve bellek tüketimi. Yeni bir instance’ın JVM’i başlatması, sınıfları yüklemesi, framework context’ini oluşturması ve [&#8230;]</p>
<p><a href="https://alikarahisar.com/blog/graalvm-native-image-cold-start-spring-boot-3/">GraalVM Native Image ile Serverless ve Mikroservislerde Cold Start Sorununu Çözmek</a> yazısı ilk önce <a href="https://alikarahisar.com/blog">Ali Karahisar</a> üzerinde ortaya çıktı.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Java; güçlü ekosistemi, olgun araçları ve yüksek çalışma zamanı performansıyla mikroservis geliştirmede en güvenilir seçeneklerden biri. Ancak konu serverless fonksiyonlar, kısa ömürlü container’lar ve hızla ölçeklenen Kubernetes iş yükleri olduğunda klasik JVM modelinin iki maliyeti daha görünür hâle gelir: başlangıç süresi ve bellek tüketimi.</p>



<span id="more-44"></span>



<p class="wp-block-paragraph">Yeni bir instance’ın JVM’i başlatması, sınıfları yüklemesi, framework context’ini oluşturması ve kodu optimize etmesi zaman alır. İstek trafiğinin olmadığı bir sürenin ardından fonksiyon yeniden oluşturuluyorsa kullanıcı bu maliyeti doğrudan “cold start” gecikmesi olarak hisseder.</p>



<p class="wp-block-paragraph">GraalVM Native Image, Java uygulamasını dağıtım öncesinde makine koduna derleyerek bu denklemi değiştirir. Uygulama, çalışma anında JAR ve tam bir JVM başlatmak yerine işletim sistemi üzerinde doğrudan çalışan platforma özgü bir executable olarak açılır. GraalVM belgelerine göre native executable yalnızca erişilebilir uygulama ve JDK kodunu, gerekli çalışma zamanı bileşenlerini ve statik olarak bağlanan yerel kodu içerir; bunun sonucu milisaniyeler düzeyinde başlangıç, warm-up gerektirmeyen performans ve daha düşük kaynak tüketimidir. (<a href="https://www.graalvm.org/latest/reference-manual/native-image/">GraalVM Native Image belgeleri</a>)</p>



<h2 class="wp-block-heading">Cold start neden oluşur?</h2>



<p class="wp-block-paragraph">Bir Java mikroservisinin ilk isteğe hazır hâle gelmeden önce tipik olarak şu işlemleri tamamlaması gerekir:</p>



<ol start="1" class="wp-block-list">
<li>JVM süreci başlatılır.</li>



<li>Uygulama sınıfları ve bağımlılıklar yüklenir.</li>



<li>Bytecode doğrulanır ve yorumlanmaya başlanır.</li>



<li>Spring ApplicationContext oluşturulur.</li>



<li>Bean tanımları, koşullu konfigürasyonlar ve proxy’ler hazırlanır.</li>



<li>Sık çalışan kod yolları belirlenir ve JIT compiler tarafından optimize edilir.</li>



<li>Bağlantı havuzları ve harici servis istemcileri hazırlanır.</li>
</ol>



<p class="wp-block-paragraph">Uzun süre çalışan bir serviste bu maliyet yalnızca bir kez ödenir ve JIT optimizasyonları zaman içinde önemli throughput avantajı sağlar. Buna karşılık serverless platformlarda, scale-to-zero kullanan Kubernetes ortamlarında veya çok sık pod oluşturan sistemlerde başlangıç maliyeti tekrar tekrar ödenebilir.</p>



<p class="wp-block-paragraph">Cold start yalnızca uygulamanın <code>main</code> metoduna ulaşma süresi değildir. Gerçek ölçüm, yeni instance’ın ilk isteğe doğru yanıt verebildiği ana kadar geçen süreyi kapsamalıdır.</p>



<h2 class="wp-block-heading">JIT ve AOT arasındaki mimari fark</h2>



<h3 class="wp-block-heading">JIT: Çalışırken öğrenen optimizasyon</h3>



<p class="wp-block-paragraph">Geleneksel JVM modelinde Java kaynak kodu önce bytecode’a çevrilir:</p>



<pre class="wp-block-code"><code>Java kaynak kodu
        ↓
     Bytecode
        ↓
 JVM + Interpreter
        ↓
   JIT Compiler
        ↓
Optimize makine kodu</code></pre>



<p class="wp-block-paragraph">JIT, uygulamanın gerçek çalışma davranışını gözlemler. Hangi metotların sık çağrıldığını, dallanmaların hangi yönde ilerlediğini ve hangi kod yollarının “hot” olduğunu belirleyerek bu bölümleri çalışma sırasında optimize eder.</p>



<p class="wp-block-paragraph">Bunun önemli bir avantajı vardır: Optimizasyon kararları gerçek trafik profilinden beslenir. Uzun süre çalışan ve CPU yoğun işlem yapan servislerde JVM, yeterli warm-up sonrasında çok yüksek tepe performansına ulaşabilir.</p>



<p class="wp-block-paragraph">Bedeli ise JVM’in, derleyicinin, profil verilerinin ve sınıf metadata’sının bellekte bulunmasıdır. Uygulama ayrıca en iyi performansına hemen değil, belirli bir ısınma süresinden sonra ulaşır.</p>



<h3 class="wp-block-heading">AOT: Çalışmadan önce tamamlanan derleme</h3>



<p class="wp-block-paragraph">GraalVM Native Image, bytecode’u build aşamasında analiz eder ve platforma özgü makine koduna dönüştürür:</p>



<pre class="wp-block-code"><code>Uygulama + bağımlılıklar + JDK sınıfları
                    ↓
      Erişilebilirlik analizi
                    ↓
       AOT optimizasyon ve derleme
                    ↓
 Kod + image heap + native runtime
                    ↓
        Native executable</code></pre>



<p class="wp-block-paragraph">Analiz <code>main</code> metodu gibi giriş noktalarından başlar. Erişilebilen sınıflar, metotlar ve alanlar tekrarlı biçimde bulunur; kullanılmadığı belirlenen parçalar native executable’a eklenmez. Derlemeden sonra yeni program elemanlarının çalışma anında sisteme eklenememesi “closed-world assumption”, yani kapalı dünya varsayımı olarak adlandırılır. (<a href="https://www.graalvm.org/latest/reference-manual/native-image/basics/">Native Image çalışma modeli</a>)</p>



<p class="wp-block-paragraph">Bazı sınıflar build sırasında initialize edilebilir. Oluşan nesneler ve statik durum “image heap” içerisine kaydedilir. Uygulama açıldığında bu işlerin yeniden yapılmaması startup süresini kısaltır.</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><tbody><tr><th>Özellik</th><th>JVM + JIT</th><th>GraalVM Native Image + AOT</th></tr><tr><td>Derleme zamanı</td><td>Çalışma sırasında devam eder</td><td>Büyük ölçüde build sırasında tamamlanır</td></tr><tr><td>Başlangıç</td><td>Class loading ve warm-up nedeniyle daha yavaş</td><td>Genellikle çok hızlı</td></tr><tr><td>Bellek</td><td>JVM, JIT ve metadata maliyeti vardır</td><td>Genellikle daha düşük RSS</td></tr><tr><td>Tepe throughput</td><td>Uzun ömürlü işlerde çok güçlü</td><td>İş yüküne göre JIT’in gerisinde kalabilir</td></tr><tr><td>Dinamik sınıf yükleme</td><td>Doğal olarak desteklenir</td><td>Kapalı dünya varsayımıyla sınırlıdır</td></tr><tr><td>Build süresi</td><td>Görece kısa</td><td>Daha uzun ve daha fazla kaynak tüketebilir</td></tr><tr><td>Dağıtım</td><td>JAR ve uyumlu JVM gerekir</td><td>İşletim sistemi ve mimariye özel binary gerekir</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">AOT her durumda JIT’ten daha hızlı demek değildir. Native Image’ın temel avantajı başlangıç ve kaynak verimliliğidir. Uzun süre çalışan, CPU yoğun servislerde JIT’in çalışma zamanı profiline dayanan optimizasyonları daha yüksek throughput sağlayabilir. GraalVM’in Profile-Guided Optimization özelliği, temsilî trafikten profil toplayıp bunu AOT derlemeye taşıyarak aradaki farkı azaltabilir. (<a href="https://www.graalvm.org/dev/reference-manual/native-image/optimizations-and-performance/">GraalVM optimizasyon belgeleri</a>)</p>



<h2 class="wp-block-heading">Startup süresi ve memory footprint üzerindeki etkisi</h2>



<p class="wp-block-paragraph">Native executable çalıştırılırken tam bir HotSpot JVM’in başlatılmasına ve uygulama kodunun JIT tarafından ısıtılmasına gerek kalmaz. Gereksiz sınıfların binary’ye alınmaması da class metadata, JIT code cache ve derleyici altyapısının çalışma zamanı maliyetlerini azaltır.</p>



<p class="wp-block-paragraph">Cloud-native sistemlerde bu özellikler üç alanda değer üretir:</p>



<ul class="wp-block-list">
<li>Serverless fonksiyonlar ilk isteğe daha kısa sürede cevap verebilir.</li>



<li>Kubernetes pod’ları daha hızlı hazır duruma gelerek scale-out gecikmesini azaltabilir.</li>



<li>Aynı bellek bütçesi içerisinde daha fazla servis instance’ı çalıştırılabilir.</li>
</ul>



<p class="wp-block-paragraph">Ancak sonucu tek bir evrensel yüzdeyle ifade etmek doğru değildir. Kazanç; kullanılan framework’e, dependency sayısına, heap ayarlarına, GC seçimine, container limitlerine ve uygulamanın başlangıç sırasında yaptığı I/O işlemlerine bağlıdır.</p>



<p class="wp-block-paragraph">Örneğin uygulama açılışta veritabanı migrasyonu çalıştırıyor veya uzak bir secret servisine bağlanıyorsa native executable bu ağ gecikmesini ortadan kaldırmaz. GraalVM kod başlatma maliyetini azaltır; dış sistemlerin gecikmesini sihirli biçimde çözmez.</p>



<p class="wp-block-paragraph">Bu nedenle JVM ve native sürümleri aynı koşullarda karşılaştırın:</p>



<pre class="wp-block-code"><code># JVM sürümü
/usr/bin/time -v java -jar target/cold-start-demo.jar

# Native sürüm
/usr/bin/time -v ./target/cold-start-demo</code></pre>



<p class="wp-block-paragraph">macOS üzerinde ayrıntılı süreç ölçümü için:</p>



<pre class="wp-block-code"><code>/usr/bin/time -l ./target/cold-start-demo</code></pre>



<p class="wp-block-paragraph">Yalnızca konsoldaki “Started” mesajını değil, aşağıdaki metrikleri kaydedin:</p>



<ul class="wp-block-list">
<li>Process başlangıcından readiness durumuna kadar geçen süre</li>



<li>İlk isteğin p50, p95 ve p99 gecikmesi</li>



<li>Warm request gecikmesi</li>



<li>Peak RSS ve steady-state RSS</li>



<li>CPU tüketimi</li>



<li>Binary veya container image boyutu</li>



<li>Native image build süresi</li>



<li>Gerçek trafik altındaki throughput</li>
</ul>



<h2 class="wp-block-heading">GraalVM Native Image’ın zorlandığı alanlar</h2>



<h3 class="wp-block-heading">1. Reflection</h3>



<p class="wp-block-paragraph">Reflection kullanılan kodda çağrılacak sınıf veya metot adı çalışma anında bir konfigürasyondan gelebilir. Statik analiz bu ilişkiyi göremiyorsa ilgili sınıfın kullanılmadığını düşünerek onu binary dışında bırakabilir.</p>



<p class="wp-block-paragraph">Sonuç olarak uygulama başarıyla derlense bile yalnızca belirli bir endpoint çağrıldığında hata verebilir. JSON serileştirme, ORM, dependency injection ve plugin tabanlı kütüphaneler bu açıdan özellikle test edilmelidir.</p>



<p class="wp-block-paragraph">Spring Boot 3’ün AOT motoru controller dönüş tipleri, bean tanımları ve pek çok framework kullanımına ait hint’leri otomatik üretir. Otomatik çıkarımın yetmediği durumlarda <code>@RegisterReflectionForBinding</code> veya <code>RuntimeHintsRegistrar</code> kullanılabilir. (<a href="https://docs.spring.io/spring-boot/reference/packaging/native-image/advanced-topics.html">Spring Boot özel native hint rehberi</a>)</p>



<p class="wp-block-paragraph">Örneğin çalışma anında JSON’a dönüştürülecek bir sınıfı açıkça kaydetmek için:</p>



<pre class="wp-block-code"><code>import org.springframework.aot.hint.annotation.RegisterReflectionForBinding;
import org.springframework.context.annotation.Configuration;

@Configuration
@RegisterReflectionForBinding(PaymentRequest.class)
class NativeConfiguration {
}</code></pre>



<h3 class="wp-block-heading">2. Dynamic proxy</h3>



<p class="wp-block-paragraph">Spring AOP, declarative transaction yönetimi ve bazı HTTP client kütüphaneleri çalışma anında proxy oluşturabilir. JVM üzerinde ihtiyaç anında üretilebilen bu sınıfların native executable içinde önceden bilinmesi gerekir.</p>



<p class="wp-block-paragraph">Spring AOT, kendi tanıyabildiği proxy’leri build sırasında üretir. Uygulamaya özgü bir JDK proxy için runtime hint eklenebilir:</p>



<pre class="wp-block-code"><code>import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;

class ApplicationRuntimeHints implements RuntimeHintsRegistrar {

    @Override
    public void registerHints(
            RuntimeHints hints,
            ClassLoader classLoader) {

        hints.proxies().registerJdkProxy(PaymentClient.class);
    }
}</code></pre>



<p class="wp-block-paragraph">Registrar’ı uygulamaya bağlayalım:</p>



<pre class="wp-block-code"><code>import org.springframework.context.annotation.ImportRuntimeHints;

@SpringBootApplication
@ImportRuntimeHints(ApplicationRuntimeHints.class)
public class PaymentApplication {

    public static void main(String&#91;] args) {
        SpringApplication.run(PaymentApplication.class, args);
    }
}</code></pre>



<p class="wp-block-paragraph">Spring’in CGLIB tabanlı proxy’leri de JVM’de çalışma anında oluşturulurken native build sürecinde önceden üretilir. Oluşturulan proxy sınıfları Maven projelerinde <code>target/spring-aot/main/classes</code> altında incelenebilir. (<a href="https://docs.spring.io/spring-boot/reference/packaging/native-image/introducing-graalvm-native-images.html">Spring AOT proxy üretimi</a>)</p>



<h3 class="wp-block-heading">3. Classpath kaynakları</h3>



<p class="wp-block-paragraph">JSON şablonları, sertifikalar, SQL dosyaları, localization bundle’ları veya statik dosyalar Java kodu olmadığı için yalnızca erişilebilirlik analiziyle her zaman bulunamaz.</p>



<p class="wp-block-paragraph">Bir kaynağı Spring runtime hints API ile eklemek mümkündür:</p>



<pre class="wp-block-code"><code>@Override
public void registerHints(
        RuntimeHints hints,
        ClassLoader classLoader) {

    hints.resources().registerPattern("templates/*.json");
    hints.resources().registerPattern("db/migration/**");
}</code></pre>



<p class="wp-block-paragraph">GraalVM ayrıca <code>META-INF/native-image</code> altındaki <code>reachability-metadata.json</code> dosyalarını okuyabilir:</p>



<pre class="wp-block-code"><code>{
  "resources": &#91;
    {
      "glob": "templates/*.json"
    }
  ]
}</code></pre>



<p class="wp-block-paragraph">Native executable içine gömülen kaynaklar gerçek bir dosya sistemi yolu olmayabilir. Bu nedenle <code>URL#getFile()</code> yerine <code>getResourceAsStream()</code> veya <code>URL#openStream()</code> tercih edilmelidir. Gerekli metadata bulunmayan dinamik erişimler çalışma zamanı hatasına dönüşebilir. (<a href="https://www.graalvm.org/jdk25/reference-manual/native-image/metadata/">GraalVM Reachability Metadata</a>)</p>



<h2 class="wp-block-heading">Spring Boot 3 ile native executable oluşturma</h2>



<p class="wp-block-paragraph">Aşağıdaki örnek Maven tabanlı bir Spring Boot 3 projesini temel alır.</p>



<h3 class="wp-block-heading">Adım 1: Projeyi oluşturun</h3>



<p class="wp-block-paragraph"><a href="https://start.spring.io/">start.spring.io</a> üzerinden şu seçimlerle bir proje oluşturabilirsiniz:</p>



<ul class="wp-block-list">
<li>Java 21 gibi desteklenen bir LTS sürümü</li>



<li>Spring Boot 3.x</li>



<li>Maven</li>



<li>Spring Web</li>



<li>GraalVM Native Support</li>
</ul>



<p class="wp-block-paragraph">Basit bir endpoint ekleyelim:</p>



<pre class="wp-block-code"><code>package com.example.coldstart;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@SpringBootApplication
public class ColdStartApplication {

    @GetMapping("/hello")
    String hello() {
        return "Hello from GraalVM Native Image";
    }

    public static void main(String&#91;] args) {
        SpringApplication.run(ColdStartApplication.class, args);
    }
}</code></pre>



<h3 class="wp-block-heading">Adım 2: Maven plugin’lerini kontrol edin</h3>



<p class="wp-block-paragraph">Spring Boot parent’ın sağladığı <code>native</code> profilinden yararlanmak için build bölümünde şu plugin’ler bulunmalıdır:</p>



<pre class="wp-block-code"><code>&lt;build&gt;
    &lt;plugins&gt;
        &lt;plugin&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-maven-plugin&lt;/artifactId&gt;
        &lt;/plugin&gt;

        &lt;plugin&gt;
            &lt;groupId&gt;org.graalvm.buildtools&lt;/groupId&gt;
            &lt;artifactId&gt;native-maven-plugin&lt;/artifactId&gt;
        &lt;/plugin&gt;
    &lt;/plugins&gt;
&lt;/build&gt;</code></pre>



<p class="wp-block-paragraph">Spring AOT işlemi ApplicationContext’i build aşamasında analiz eder; bean tanımlarına ve kapalı dünya varsayımına uygun optimize edilmiş kod ile GraalVM hint dosyaları üretir. Maven çıktıları <code>target/spring-aot</code> altında incelenebilir. (<a href="https://docs.spring.io/spring-boot/3.3/maven-plugin/aot.html">Spring Boot AOT belgeleri</a>)</p>



<h3 class="wp-block-heading">Adım 3: Yerel native executable üretin</h3>



<p class="wp-block-paragraph">Makinede kullandığınız Java sürümüyle uyumlu bir GraalVM dağıtımı ve gerekli native toolchain kurulu olmalıdır. Ardından:</p>



<pre class="wp-block-code"><code>./mvnw -Pnative native:compile</code></pre>



<p class="wp-block-paragraph">Executable varsayılan olarak <code>target</code> dizininde oluşur:</p>



<pre class="wp-block-code"><code>./target/cold-start-demo</code></pre>



<p class="wp-block-paragraph">Servisi doğrulayın:</p>



<pre class="wp-block-code"><code>curl http://localhost:8080/hello</code></pre>



<p class="wp-block-paragraph">Gradle kullanan projelerde GraalVM Native Build Tools plugin’i uygulandıktan sonra eşdeğer komutlar şunlardır:</p>



<pre class="wp-block-code"><code>./gradlew nativeCompile
./build/native/nativeCompile/cold-start-demo</code></pre>



<h3 class="wp-block-heading">Adım 4: Buildpack ile native container image oluşturun</h3>



<p class="wp-block-paragraph">Yerel GraalVM kurulumu yerine Docker ve Cloud Native Buildpacks kullanabilirsiniz:</p>



<pre class="wp-block-code"><code>./mvnw -Pnative spring-boot:build-image</code></pre>



<p class="wp-block-paragraph">Oluşan image’ı çalıştırın:</p>



<pre class="wp-block-code"><code>docker run --rm -p 8080:8080 \
  docker.io/library/cold-start-demo:0.0.1-SNAPSHOT</code></pre>



<p class="wp-block-paragraph">Gradle karşılığı:</p>



<pre class="wp-block-code"><code>./gradlew bootBuildImage</code></pre>



<p class="wp-block-paragraph">Spring Boot, Maven ve Gradle üzerinden Paketo buildpack ile JVM içermeyen native container image üretilmesini destekler. (<a href="https://docs.spring.io/spring-boot/3.4/how-to/native-image/developing-your-first-application.html">Spring Boot native uygulama rehberi</a>)</p>



<p class="wp-block-paragraph">Native Image doğrudan cross-compilation yapmadığından executable hedef işletim sistemi ve CPU mimarisi için üretilmelidir. Örneğin Linux AMD64 ile Linux ARM64 artefact’larını ayrı runner’larda veya uygun multi-platform build altyapısında oluşturmak gerekir.</p>



<h3 class="wp-block-heading">Adım 5: Native test çalıştırın</h3>



<p class="wp-block-paragraph">Normal JVM testleri hızlı geri bildirim sağlar; ancak kapalı dünya varsayımından kaynaklanan sorunları yakalamak için native test de CI sürecine eklenmelidir:</p>



<pre class="wp-block-code"><code>./mvnw -Pnative native:test</code></pre>



<p class="wp-block-paragraph">Gradle için:</p>



<pre class="wp-block-code"><code>./gradlew nativeTest</code></pre>



<p class="wp-block-paragraph">Özellikle şu yolları test edin:</p>



<ul class="wp-block-list">
<li>JSON serialization ve deserialization</li>



<li>Reflection kullanan kod</li>



<li>AOP ve transaction proxy’leri</li>



<li>Dosya, template ve resource bundle erişimi</li>



<li>SSL ve güvenlik sağlayıcıları</li>



<li>ORM entity’leri</li>



<li>Dinamik olarak seçilen implementasyonlar</li>
</ul>



<h3 class="wp-block-heading">Adım 6: Eksik metadata’yı tracing agent ile bulun</h3>



<p class="wp-block-paragraph">Spring’in otomatik hint üretiminin yetmediği eski veya uyumsuz bir kütüphanede uygulamayı önce JVM üzerinde tracing agent ile çalıştırabilirsiniz:</p>



<pre class="wp-block-code"><code>java -Dspring.aot.enabled=true \
  -agentlib:native-image-agent=config-output-dir=target/native-agent \
  -jar target/cold-start-demo.jar</code></pre>



<p class="wp-block-paragraph">Uygulamanın kritik akışlarını çalıştırdıktan sonra oluşan metadata’yı inceleyin. Gerekli dosyalar kontrollü biçimde <code>src/main/resources/META-INF/native-image/</code> altına taşınabilir.</p>



<p class="wp-block-paragraph">Tracing agent yalnızca çalıştırılan yolları görür. Eksiksiz test kapsamı yoksa gerekli metadata’nın bir kısmı yine atlanabilir; üretilen dosyaları körlemesine kabul etmek yerine sadeleştirmek ve doğrulamak gerekir. GraalVM’in agent’ı reflection, proxy, JNI ve resource erişimlerini kaydedebilir. (<a href="https://www.graalvm.org/jdk25/reference-manual/native-image/metadata/AutomaticMetadataCollection/">GraalVM tracing agent rehberi</a>)</p>



<h2 class="wp-block-heading">Üretim ortamı için mimari öneriler</h2>



<p class="wp-block-paragraph">GraalVM Native Image özellikle şu iş yüklerinde güçlü bir seçenektir:</p>



<ul class="wp-block-list">
<li>Trafiği düzensiz serverless fonksiyonlar</li>



<li>Scale-to-zero kullanan servisler</li>



<li>Çok hızlı yatay ölçeklenmesi gereken API’ler</li>



<li>Düşük bellek limitli container’lar</li>



<li>CLI uygulamaları ve kısa ömürlü batch işler</li>



<li>Edge veya yoğun çoklu servis ortamları</li>
</ul>



<p class="wp-block-paragraph">Buna karşılık aşağıdaki durumlarda geleneksel JVM seçeneğini ayrıca benchmark etmek gerekir:</p>



<ul class="wp-block-list">
<li>Günlerce çalışan CPU yoğun servisler</li>



<li>Yoğun dinamik class loading veya plugin mimarisi</li>



<li>Native desteği zayıf üçüncü taraf kütüphaneler</li>



<li>Çok sık değişen runtime bean yapıları</li>



<li>Derleme süresi ve CI kaynak maliyetinin kritik olduğu projeler</li>
</ul>



<p class="wp-block-paragraph">İyi bir cloud-native strateji bütün servisleri zorunlu olarak native’e çevirmek değildir. Cold start ve bellek baskısı yaşayan sınır servislerini native executable olarak çalıştırırken, uzun ömürlü ve throughput odaklı servisleri JVM üzerinde bırakmak daha dengeli olabilir.</p>



<h2 class="wp-block-heading">Sonuç</h2>



<p class="wp-block-paragraph">GraalVM Native Image, Java’nın serverless ve mikroservis mimarilerindeki en görünür dezavantajlarından ikisini doğrudan hedefler: cold start gecikmesi ve yüksek memory footprint.</p>



<p class="wp-block-paragraph">AOT derleme; erişilebilir kodu, gerekli runtime bileşenlerini ve belirli başlangıç durumlarını build aşamasında native executable içine taşır. Böylece uygulama hızlı açılır, warm-up ihtiyacı azalır ve daha küçük bir çalışma zamanı belleğiyle hizmet verebilir.</p>



<p class="wp-block-paragraph">Bu kazanımın karşılığında closed-world assumption nedeniyle reflection, dynamic proxy, serialization ve classpath kaynaklarının daha bilinçli yönetilmesi gerekir. Spring Boot 3’ün AOT altyapısı bu işin büyük bölümünü otomatikleştirir; <code>RuntimeHints</code>, reachability metadata, tracing agent ve native testler ise kalan boşlukları kapatır.</p>



<p class="wp-block-paragraph">Doğru karar için son adım her zaman aynıdır: JVM ve native sürümü gerçek trafik profiliniz, hedef container limitleriniz ve ölçeklenme modeliniz altında ölçün. Cold start kritikse GraalVM Native Image çoğu zaman yalnızca bir optimizasyon değil, doğrudan mimari bir avantajdır.</p>



<h2 class="wp-block-heading">Sık Sorulan Sorular</h2>



<h3 class="wp-block-heading">GraalVM Native Image tamamen JVM’siz mi çalışır?</h3>



<p class="wp-block-paragraph">Üretilen executable’ın yanında ayrı bir HotSpot JVM bulunmaz. Ancak binary; garbage collector, thread desteği ve gerekli çalışma zamanı servisleri gibi Native Image runtime bileşenlerini içerir.</p>



<h3 class="wp-block-heading">Native Image her Java uygulamasında daha hızlı mıdır?</h3>



<p class="wp-block-paragraph">Hayır. Başlangıç süresi genellikle çok daha iyidir; fakat uzun süre çalışan ve CPU yoğun uygulamalarda JIT daha yüksek tepe throughput sağlayabilir. Karar benchmark sonucuna göre verilmelidir.</p>



<h3 class="wp-block-heading">Spring Boot 3 reflection sorunlarını otomatik çözer mi?</h3>



<p class="wp-block-paragraph">Spring AOT birçok yaygın kullanım için gerekli hint’leri otomatik üretir. Uygulamaya özel reflection, dinamik proxy veya üçüncü taraf kütüphane kullanımlarında ek runtime hint gerekebilir.</p>



<h3 class="wp-block-heading">Native executable bir kez üretilip her platformda çalıştırılabilir mi?</h3>



<p class="wp-block-paragraph">Hayır. Binary hedef işletim sistemi ve CPU mimarisine özeldir. Linux, macOS, AMD64 ve ARM64 hedefleri için uygun ortamlarda ayrı build alınmalıdır.</p>



<h3 class="wp-block-heading">GraalVM Native Image serverless maliyetini azaltır mı?</h3>



<p class="wp-block-paragraph">Daha hızlı startup ve düşük bellek tüketimi, platformun fiyatlandırma modeline bağlı olarak süre ve kaynak maliyetini azaltabilir. Kesin kazanç ancak gerçek provider, trafik ve bellek ayarlarıyla yapılan ölçüm sonucunda belirlenebilir.</p>



<p class="wp-block-paragraph"></p>
<p><a href="https://alikarahisar.com/blog/graalvm-native-image-cold-start-spring-boot-3/">GraalVM Native Image ile Serverless ve Mikroservislerde Cold Start Sorununu Çözmek</a> yazısı ilk önce <a href="https://alikarahisar.com/blog">Ali Karahisar</a> üzerinde ortaya çıktı.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://alikarahisar.com/blog/graalvm-native-image-cold-start-spring-boot-3/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
