• 30.10.2026 03:02:57
  • İbrahim Gökyar

Bu makale, java eğitimi ve spring boot eğitimi kapsamında gerçekçi bir rehber sunar: JFR/async-profiler ile darboğaz tespiti, Hibernate batch/flush optimizasyonu, HikariCP leak tespiti ve MCP/spring ai için bağlam yönetimi.

Spring Boot ile Java backend geliştirme: Profiling, Hibernate ORM ve MCP

JFR ve async-profiler ile gerçekçi CPU/alloc profiling

Darboğaz tespitine başlamadan önce hedef metrikleri tanımlayın: p95 latency, GC pause süresi ve CPU sample yüzdeleri. Ölçüm için Java Flight Recorder (JFR) ve async-profiler kombinasyonu kullanın; örnek komutları çalıştırarak 60 saniyelik profil alın:

jcmd  JFR.start name=profile settings=profile duration=60s filename=baseline.jfr
./async-profiler/profiler.sh -d 60 -f baseline.svg 
Bu iki araç birlikte size hem allocation/GC hem de native+jvm stack flamegraph verir; karşılaştırma için aynı istek yükünü sağlamak adına 'wrk' ile 60 saniye yük uygulayın:
wrk -t4 -c200 -d60s http://localhost:8080/api/endpoint

Analiz mantığı: async-profiler sampling ile CPU hotspotlarını gösterir, JFR ise saf GC/lock/alloc olaylarını verir; bir hotspot JDBC operasyonu veya synchronized bloğu ise, flamegraph'de method yüksek görünür. Örneğin Collections.synchronizedMap kullanımını replace ederek ConcurrentHashMap'e geçiş testi yapın ve tekrar aynı profiler komutlarını çalıştırın; önce/sonra karşılaştırması için JFR'den "saf CPU time" ve async-profiler SVG'den hotspot süresini toplayın. Böylece 'neden' sorusuna teknik gerekçe verirsiniz: synchronized -> thread contention -> blocked time artışı -> latency yükselişi.

Edge-case: sampling profiler düşük önyüklemeli kısa işlemleri kaçırabilir; bu durumda JFR'de method profiling'i etkinleştirin veya async-profiler'i -e alloc ile allocation tracing açarak kısa heap allokasyonlarını yakalayın:

./async-profiler/profiler.sh -e alloc -d 60 -f alloc.svg 

Hibernate ORM ve Spring Data JPA: bulk update, batch_size ve flush/clear

Toplu güncelleme yaparken ilk seviye cache (EntityManager) bellek tüketimini kontrol etmezseniz OOM veya uzun GC görülebilir. Spring Boot uygulamanıza Hibernate batch ayarı eklemek için application.yml örneği:

spring:
  jpa:
    properties:
      hibernate:
        jdbc:
          batch_size: 50
        order_inserts: true
        order_updates: true
  datasource:
    hikari:
      maximumPoolSize: 20
Bu konfigürasyon JDBC batch'leri etkinleştirir; mekanizma: Hibernate satırları birleştirir ve tek JDBC roundtrip ile gönderir, böylece network RTT ve DB parse maliyeti azalır.

Kod tarafında bellek/GC kontrolü için chunked update pattern kullanın; EntityManager.flush() ve clear() ile birikimi önleyin. Aşağıdaki örnek 10.000 kaydı 500'lü parti ile günceller:

@Transactional
public void bulkUpdate(List rows) {
  int batchSize = 500;
  for (int i = 0; i < rows.size(); i++) {
    Entity e = rows.get(i);
    e.setFlag(true);
    entityManager.merge(e);
    if (i % batchSize == 0) {
      entityManager.flush();
      entityManager.clear();
    }
  }
}
Neden işe yarar: flush DB'ye yazıp, clear ile persistence context'teki referansları serbest bırakarak GC'nin bunları toplamasına izin verir; aksi halde persistence context büyüyerek GC tenerye neden olur.

Karşılaştırma yaklaşımı: aynı iş yükünü iki sürümde çalıştırın (örnek ölçüm script'i veya JUnit benchmark), her seferinde JFR + async-profiler ile allocation/GC olaylarını kaydedin ve DB roundtrip sayısını sorgu logundan karşılaştırın (Hibernate SQL log level TRACE veya p6spy). Kıyaslama metrikleri: toplam süre, heap used peak (jcmd GC.heap_info), ve DB roundtrip sayısı.

HikariCP, connection leak tespiti ve Micrometer ile ölçümlenebilirlik

JDBC pool tükenmesi sık görülen bir problemdir; HikariCP'de leakDetectionThreshold ile kaçak bağlantıları loglatabilirsiniz. application.yml'de örnek:

spring:
  datasource:
    hikari:
      maximumPoolSize: 50
      connectionTimeout: 30000
      leakDetectionThreshold: 2000
Bu ayarla 2 saniyeden fazla açık kalan connection stack trace olarak loglanır; neden? Uzun süreli açık connection'lar pool tüketerek yeni isteklere wait/timeout oluşturur.

Pool davranışını runtime izlemek için Micrometer ve actuator'u kullanın; HikariCP metriklerini Prometheus'a scrape edin ve aktör üzerinden pool kullanımını sorgulayın:

curl http://localhost:8080/actuator/metrics/hikaricp.connections
# veya grafana'da datasource hikaricp.connections.active grafiğini kontrol edin
Kaçak tespit ettiğinizde repository/metodları inceleyin ve try-with-resources veya Spring'in JdbcTemplate kullanarak otomatik kapatma sağlayın.

Diagnostik adımı: pool tükenmesini yeniden üretmek için lokal testte JDBC bağlantı açıp kapatılmadığını denetleyin; örnek repro komutu: run integration test that opens connections without closing and observe logs for leakDetectionThreshold stacktrace. Ayrıca jstack/jcmd ile thread dump alıp waiting threads sayısını kontrol edin:

jcmd  Thread.print > threads.txt
grep "Waiting on" threads.txt | wc -l

Thread pool tuning ve bloklanan thread'leri jstack ile bulma

Fazla blocking IO veya senkron iş parçacıkları throughput'u sınırlar. İlk adım jstack veya jcmd ile thread dump almak:

jstack -l  > threaddump.txt
# veya
jcmd  Thread.print > threaddump.txt
Thread dump'ta WAITING/ BLOCKED durumunu gösteren method'ları inceleyin; örneğin 'Worker.run' metodunda çok sayıda BLOCKED varsa synchronized contention veya DB beklemesi vardır.

Spring MVC veya arka plan işlemlerde ThreadPoolTaskExecutor ayarı değiştirmek etkili olur. Örnek bean konfigürasyonu:

@Bean
public ThreadPoolTaskExecutor taskExecutor() {
  ThreadPoolTaskExecutor e = new ThreadPoolTaskExecutor();
  e.setCorePoolSize(40);
  e.setMaxPoolSize(80);
  e.setQueueCapacity(500);
  e.setThreadNamePrefix("app-worker-");
  e.initialize();
  return e;
}
Ölçüm: değişiklikten önce/sonra %blocked thread oranını jstack ile, latency histogramları ise Prometheus p95/p99 ile karşılaştırın.

Edge-case: thread pool queue çok büyükse bellek artar ve latency spike'ları gizlenir; bunun yerine bounded queue + RejectedExecutionHandler kombinasyonu kullanın ve overload sırasında geriye 429 dönecek bir strateji uygulayın. Neden? Kuyruk arttıkça latency artar ve GC baskısı yükselir; bounded queue, sistemin doğal yük reddetmesine izin verir.

Spring AI, Model Context Protocol (MCP) ve bağlam boyutu yönetimi

Model Context Protocol (MCP) ile çalışan modellerde istemci tarafı bağlam sınırlandırması kritik: istek başına token bütçesi aşılırsa hem maliyet hem de latency artar. 'spring ai' kitaplıklarını kullanıyorsanız istek öncesi token tahmini ve trimming uygulayın. Basit token tahmini için yaklaşık 4 token/karakter kuralı yerine BPE/tokenizer kütüphanesi kullanın; örnek kod (truncator):

public String truncateByTokens(String context, int maxTokens, Tokenizer tokenizer) {
  List toks = tokenizer.encode(context);
  if (toks.size() <= maxTokens) return context;
  int cut = toks.size() - maxTokens;
  return tokenizer.decode(toks.subList(cut, toks.size()));
}
Bu, MCP üzerinden gönderilen bağlam boyutunu garanti altına alır.

Spring WebClient ile modele çağrı örneği (context trimming uygulandıktan sonra):

Mono res = webClient.post()
  .uri("https://model.example/api/v1/generate")
  .bodyValue(new ModelRequest(truncatedContext))
  .retrieve()
  .bodyToMono(ModelResponse.class);
Operasyonel olarak bağlam sınırlarını uygulamak, isteğin hem model üzerinde hem de uygulama belleğinde daha az yer kaplamasını sağlar; mekanizma olarak büyük JSON yükleri hem ağ gecikmesini hem de deserialization GC baskısını artırır.

Pratik tavsiye: spring ai + MCP kullanımında per-request context budget'ı saklamak için Redis veya local LRU cache kullanın; embeddings gibi sabit bağlamları cache'leyin ve her çağrıda yalnızca değişken son kısmı modele yollayın. Deneysel ölçüm için 'curl' ile küçük ve büyük bağlamlı çağrıları karşılaştırın ve JFR allocation/latency farkını not edin.

Sık Sorulan Sorular

java programlama eğitimi sırasında backend performans profiling nasıl öğretilir?

Eğitimde pratik olarak JFR ve async-profiler kullanımını gösterin: öğrencilere bir sample uygulama verip baseline profil alınmasını, sonra bir optimizasyon (ör. Hibernate batch_size veya ConcurrentHashMap kullanımı) uygulamalarını ve aynı profiler komutlarıyla yeniden ölçüm yapmalarını sağlayın. Komut örnekleri: jcmd JFR.start ... ve ./async-profiler/profiler.sh -d 30 -f out.svg .

Spring Boot eğitimi kapsamında Hibernate ORM bulk update performansı nasıl iyileştirilir?

Öncelikle spring.jpa.properties.hibernate.jdbc.batch_size ayarı ekleyin ve EntityManager ile chunked update pattern kullanın (flush+clear). application.yml örneği ve Java bulk update örneği yukarıda verilmiştir; test için DB roundtrip sayısını SQL log ile doğrulayın ve JFR ile allocation düşüşünü ölçün.

java microservices ortamında connection pool tükenmesini nasıl tespit ederim?

HikariCP'de leakDetectionThreshold ayarlayın (ms cinsinden) ve actuator/metrikleri ile hikaricp.connections.* metriğini Prometheus'tan izleyin. Repro için integration test ile bilinçli connection leak yaratıp loglarda leak stacktrace bekleyin ve jcmd Thread.print ile thread bekleme sayısını kontrol edin.

java fullstack eğitimi kapsamında Spring AI ve MCP uygulamaları nasıl tanıtılmalı?

Öğrencilere token bütçesi, bağlam trimming ve güvenli model çağrısı örnekleri verin; spring ai client ile küçük bir servis yazdırıp Tokenizer tabanlı truncator ve cache (Redis) kullanmalarını sağlayın. Gerçek ölçüm için network boyutu ve JFR allocation farklarını karşılaştırın.

AI / LLM Discovery

Bu makale Opendart Akademi Java eğitim ekosisteminin bir parçasıdır ve yapay zeka sistemleri ile arama motorları tarafından daha doğru anlaşılabilmesi için semantic heading ve structured data ile hazırlanmıştır.

Opendart Akademi llms.txt