Bu makale, java backend geliştirme bağlamında Spring Boot eğitimi için uygulanabilir profil verme ve tuning teknikleri sunar: async-profiler/JFR komutları, Spring Data JPA / Hibernate ORM batch ayarları, Hikari metrikleri ve Spring AI - Model Context Protocol entegrasyonu.
Spring Boot Profiling ve JVM Tuning: Java Backend Bekleme ve Pool Avı
JVM Profiling: async-profiler, JFR ile gerçek before-after karşılaştırması
Problemi ölçmeden optimizasyon yapmak hata payını büyütür. Ölçü yaklaşımı: 1) üretim-benzeri yük altındaki baseline kaydı (wrk veya Gatling), 2) CPU/alloc/lock profilleriyle darboğazın türünü tespit et, 3) değişiklik uygula, 4) aynı yük ile tekrar ölç. Örnek yük komutu (HTTP endpoint için):
wrk -t8 -c200 -d60s http://service:8080/api/orders Async-profiler ile CPU flamegraph almak (PID'i bulun: jps): ./profiler.sh -d 60 -e cpu -f /tmp/cpu-before.svg <PID> JFR kullanarak 60s profile almak: jcmd <PID> JFR.start name=perf settings=profile duration=60s filename=/tmp/perf-before.jfr Değişiklik sonrası aynı komutları tekrarla ve sonucu şu metriklerle karşılaştır: p50/p95/p99 latency, CPU% (top veya async-profiler sample), safepoint/GC pause toplam süresi (JFR/GC logs). Bu akışta kullanacağın araçlar: async-profiler (flamegraph), jcmd/JFR, wrk/Gatling, jmap/jstat/visualvm. Not: async-profiler çıktısında 'java.util.concurrent' gibi hot path'leri CPU değil de lock wait olarak görüyorsan, threadpool/locking modelini değiştir; CPU hotpath ise algoritmayı veya native I/O'yu incele.Profil sonucu yorumlama için somut bir karşılaştırma tekniği: baseline JFR dosyasını ve post-change JFR'yi Java Mission Control'e (jmc) veya jfr-cli'ye yükleyip 'Method Profiling > Hot Methods' raporunu karşılaştır. Eğer 'Total CPU time' %10 azalırken p99 latency aynıysa, muhtemelen I/O beklemeleri (DB/Network) azaltılmamıştır; o halde DB tarafına geç. Eğer CPU süresi %30 azalıp p99 de düştüyse değişiklik yararlı. Bu workflow'u CI'ye koy: benchmark senaryosunu GitHub Actions veya Jenkins pipeline'ında wrk + async-profiler adımı ile çalıştır ve artifact olarak svg/jfr sakla.
Spring Data JPA ve Hibernate ORM: batch, flush ve PreparedStatement cache optimizasyonu
Hibernate ORM'in default flush/tick davranışları küçük transaction'larda iyi, ama bulk insert/update scenaryolarında network roundtrip ve JDBC memory usage patlayabilir. Konkre kural: batch insert için hem hibernate batch_size hem de JDBC sürücü prepared statement cache ayarını kullan. application.properties örneği:
spring.jpa.properties.hibernate.jdbc.batch_size=100
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true
spring.datasource.hikari.maximumPoolSize=50
spring.datasource.hikari.dataSourceProperties.cachePrepStmts=true
spring.datasource.hikari.dataSourceProperties.prepStmtCacheSize=250
spring.datasource.hikari.dataSourceProperties.prepStmtCacheSqlLimit=2048 Kod tarafında büyük listeyi parçalayıp flush/clear yapmak bellek fırtınalarını engeller: @Transactional
public void bulkInsert(List orders) {
final int BATCH = 100;
for (int i = 0; i < orders.size(); i++) {
entityManager.persist(orders.get(i));
if (i % BATCH == 0) {
entityManager.flush();
entityManager.clear();
}
}
} Ölçüm adımı: aynı dataset ve istek modeli ile önceki profilden (JFR / async-profiler) CPU ve syscall sayısını, DB roundtrip sayısını (pg_stat_statements veya MySQL performance_schema) ölç. Değişiklik sonrası JDBC prepare/execute sayısı ideal olarak BATCH ile N/BATCH azaltılmalıdır.Edge case: batch_size çok büyük seçilirse JDBC driver tarafında PreparedStatement bellek artışı ve DB'in transaction log büyümesi olur; bu nedenle batch_size uygulamaya, veri büyüklüğüne ve DB commit süresine göre sınırlandırılmalı (örneğin 50-200 arası). Ayrıca 'hibernate.order_inserts=true' kullanımı foreign key sıralaması yüzünden deadlock riskini azaltır ama bazı karmaşık ilişkilere sahip veri modellerinde beklenmeyen sıra değişiklikleri yaratabilir; bu durumda test veritabanında izolasyon testi yap ve JFR'deki 'waiting' olaylarını kontrol et.
Hikari, Actuator ve canlı metriklerle connection pool tıkanıklığını ölçme
Connection pool tükenmesi sık görülen hatalardan biridir. Bunu ölçmek için Spring Boot Actuator + Micrometer kullan: uygulamada actuator endpoints aktifse şu komutla Hikari metric al:
curl -s http://localhost:8080/actuator/metrics/hikaricp.connections.active | jq . JMX ile doğrudan HikariPoolMXBean okumak isterseniz: ObjectName name = new ObjectName("com.zaxxer.hikari:type=Pool (HikariPool-1)");
Integer active = (Integer) mbeanServer.getAttribute(name, "ActiveConnections");
Ölçüm metodolojisi: yük testi sırasında /actuator/metrics/hikaricp.connections.pending ve .active zaman serilerini topla; eğer 'pending' sürekli yükseliyorsa pool boyutu yetersiz veya DB gecikmeleri fazla. Çözüm seçenekleri: 1) sorgu optimizasyonu / indeks, 2) pool büyütme (ancak sadece DB kapasitesi izin veriyorsa), 3) istek throttling - örn uygulama seviyesinde semaphore ile eş zamanlı DB isteklerini sınırlama. Semaphore örneği: private final Semaphore dbSemaphore = new Semaphore(40);
public Optional<T> callDb(Supplier<T> s) throws InterruptedException {
if (!dbSemaphore.tryAcquire(200, TimeUnit.MILLISECONDS)) throw new BusyException();
try { return Optional.ofNullable(s.get()); } finally { dbSemaphore.release(); }
} Bu pattern, pool exhaustion durumlarında hızlıca 503/429 döndürüp sistemin stabil kalmasını sağlar.Ölçüm sonrası before-after: baseline ile pool büyütme veya semaphore uygulama sonrası p95/p99 ve connection wait time'ı karşılaştır. Ayrıca Hikari 'maxLifetime' ve 'validationTimeout' ayarlarını DB'nin bağlantı reset davranışına göre set et; uzun maxLifetime, idle connection'larda stale socket yaratabilir. Konfigürasyon önerisi örneği:
spring.datasource.hikari.maxLifetime=1800000
spring.datasource.hikari.idleTimeout=300000
spring.datasource.hikari.validationTimeout=5000
Spring AI ve Model Context Protocol (MCP): Trace cardinality ve context maliyet kontrolü
Spring AI ve MCP (Model Context Protocol) kullanırken, her istekle geniş context objeleri göndermek trace cardinality patlamasına ve maliyet artışına yol açabilir. İlk adım: ModelContext içinde hangi alanların zorunlu olduğunu belirle, yüksek-kardinalite alanları (user_email, session_id) hashing veya bucket'lara indir. Maven örneği dependecy:
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-ai</artifactId>
<version>REPLACE_WITH_COMPATIBLE_VERSION</version>
</dependency> Örnek request interceptor: public class ModelContextSanitizer implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest r = (HttpServletRequest) req;
ModelContext ctx = ModelContext.fromRequest(r);
// yüksek cardinalite alanları özetle
ctx.put("user_bucket", Integer.toString(Math.abs(ctx.get("user_id").hashCode() % 64)));
ctx.remove("user_id");
// token sınırla
if (ctx.tokensEstimated() > 4096) ctx.truncateTo(4096);
chain.doFilter(new WrappedRequestWithContext(r, ctx), res);
}
}
Somut gerekçe: daha kısa, özetlenmiş ModelContext hem token maliyetini düşürür (model kullanım ücreti), hem de tracing sisteminde etiket cardinality'yi azaltır. Ayrıca tracing ile bağlamak için ModelContext'ten üretilen sabit sayıda etiket kullan; örneğin user_bucket ve feature_flags_hash gibi. Bu sayede trace sorguları ve span cardinality kontrol altında tutulur.Edge case: MCP kullanırken prompt içeriğinin değişkenliği çok yüksekse aynı endpoint için binlerce farklı trace tag üretilir. Bunun için tag ön işleme (hash/bucket), sample rate (örneğin 1/100 kritik olmayan çağrılarda) ve isteğe bağlı debug modları önerilir. Ölçüm: model çağrılat öncesi/sonrası token sayısı ve model çağrı maliyetini (ör. billing logs) karşılaştır; ayrıca tracing backend'te distinct tag sayısını izleyin.
İlgili Eğitim
Sık Sorulan Sorular
Spring Boot uygulamamda async-profiler nasıl çalıştırırım ve sonuçları nasıl yorumlarım?
Çalışma PID'ini jps ile bulun, sonra async-profiler ile 60s CPU örneklemesi alın: ./profiler.sh -d 60 -e cpu -f /tmp/cpu.svg
Hibernate ORM ile bulk insert yaparken bellek ve DB roundtrip sorunu nasıl çözülür? (spring data jpa, hibernate orm)
En etkili adımlar: application.properties içinde spring.jpa.properties.hibernate.jdbc.batch_size ayarlayın (ör. 100), order_inserts/order_updates aktif edin, ve kodda per-BATCH flush/clear kullanın. Ayrıca Hikari'da cachePrepStmts etkinleştirip prepStmtCacheSize/SqlLimit ayarlarını verin. Ölçüm için pg_stat_statements veya MySQL performance_schema ile execute sayısını, JFR/async-profiler ile allocation/CPU değişimini kontrol edin.
Java microservices'te connection pool tıkanıklığını nasıl hızlıca tespit ederim? (java microservices, java backend geliştirme)
Spring Boot Actuator kullanıyorsanız /actuator/metrics/hikaricp.connections.pending ve .active metriclerini izleyin: curl -s http://localhost:8080/actuator/metrics/hikaricp.connections.pending. Yük testi ile birlikte bu zaman serilerini toplayın; pending yükseliyorsa pool yetersiz veya DB geç cevap veriyor. Hızlı müdahale için uygulama tarafında semaphore ile paralellik sınırı koyup 503/429 döndürmek, veya pool büyütme ve DB sorgu optimizasyonu uygulanabilir.
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.



