• 29.10.2026 04:56:14
  • İbrahim Gökyar

Java backend geliştirme için pratik profiling ve Hibernate optimizasyonları. Bu makale, spring boot eğitimi bağlamında JFR, async-profiler, Spring Data JPA ve WebClient ile ölçülebilir performans iyileştirmeleri gösterir.

Spring Boot Performans Avı: Java Backend Geliştirme ve Profiling

JVM Profiling ile Baseline Alımı (JFR ve async-profiler)

İlk adım her zaman ölçmek: üretim benzeri yük altında 'baseline' kaydı alın. Gerçek araçlar: Java Flight Recorder (JFR) ve async-profiler. JFR ile 60 sn kayıt almak için:

jcmd  JFR.start name=baseline settings=profile duration=60s filename=baseline.jfr
# Sonra kaydı durdurmak veya indirmek için
jcmd  JFR.dump name=baseline filename=baseline_dump.jfr
async-profiler ile CPU flamegraph üretmek için shell aracını kullanın:
./profiler.sh -d 60 -e cpu -f baseline.svg 
Neden bu ikisi birlikte? JFR GC, safepoint ve allocation verisi verir; async-profiler native CPU örneklemesiyle Java frame'lerini doğru şekilde gösterir. Karşılaştırma yöntemi: 1) baseline jfr ve baseline.svg; 2) yapısal değişiklik (ör. hibernate.jdbc.batch_size=50 veya connection pool ayarı); 3) aynı yükü tekrar çalıştır ve yeni jfr/new.svg oluştur; 4) flamegraph boyutlarını ve JFR allocation istatistiklerini karşılaştırarak belirli metodun CPU payını ve allocation rate değişimini yüzde olarak raporla. Kullanılacak komutlar ve ölçütler somut: toplam CPU time, top-5 JVM metodu CPU yüzdesi, GC allocation rate (bytes/s).

Hibernate ORM: JDBC Batch, ID stratejileri ve saveAll chunking

Yazma yolunda en sık yapılan hata: batching etkin değilken saveAll kullanmak. Spring Data JPA ve Hibernate için uygulama ayar örneği:

spring.jpa.properties.hibernate.jdbc.batch_size=50
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true
spring.datasource.hikari.maximum-pool-size=20
Önemli nokta - IDENTITY kullanan PK ile JDBC batch çalışmaz; bu yüzden sequence tabanlı ID veya UUID kullanın:
@Entity
public class Order {
  @Id
  @GeneratedValue(strategy = GenerationType.SEQUENCE)
  private Long id;
  // ...
}

// repository kullanımında chunking
public void saveInBatches(List items, int batchSize) {
  for (int i = 0; i < items.size(); i += batchSize) {
    List chunk = items.subList(i, Math.min(i + batchSize, items.size()));
    repo.saveAll(chunk);
    entityManager.flush();
    entityManager.clear();
  }
}
Neden: JDBC batch ile round-trip sayısı ve prepareStatement çağrısı azalır; ancak EntityManager.flush ve clear yapılmazsa memory ve 2nd-level-cache baskısı nedeniyle GC/Full GC artar. Ölçüm yaklaşımı: async-profiler veya JFR ile allocations/samples karşılaştır, ayrıca Hibernate statistics ile prepareStatementCount ölç.

Sorgu Profiling: Hibernate Statistics, p6spy ve EntityGraph ile N+1 tespiti

N+1 problemini ölçüp düzeltmek için somut araçlar: hibernate.generate_statistics, p6spy veya datasource-proxy, ve kod tarafında @EntityGraph veya join fetch kullanımı. Uygulama.properties örneği:

spring.jpa.properties.hibernate.generate_statistics=true
# p6spy eklemek için Maven/Gradle dependency ve spy.properties yapılandırması gereklidir
Hibernate istatistiklerini programatik okumak için:
SessionFactory sf = entityManager.unwrap(Session.class).getSessionFactory();
Statistics stats = sf.getStatistics();
long sqlCount = stats.getPrepareStatementCount();
long entityFetchCount = stats.getEntityFetchCount();
logger.info("prepareStatements={}, entityFetches={}", sqlCount, entityFetchCount);
Kaç sorgu azaldı diye ölçün: baseline prepareStatementCount ve optimize sonrası count farkı size net sayı verir. N+1'yi düzeltmek için @EntityGraph kullanımı örneği:
@EntityGraph(attributePaths = {"items", "customer"})
@Query("select o from Order o where o.id = :id")
Order findByIdWithRelations(Long id);
Edge case: lazy koleksiyonun büyük olması durumunda eager fetch bellek patlamasına yol açabilir; bunun için sayfalama veya özel DTO fetch (select new) tercih edin ve sonuçları stream yerine page ile işleyin.

Spring Boot Mikroservisleri: WebClient, Reactor Netty ve Thread Pool Tuning

Ağ I/O ve thread tıkanmaları için somut kontroller: Reactor Netty connection pool, Hikari leakDetectionThreshold, jstack ve async-profiler wall/lock eventleri. WebClient örneği Reactor Netty ile connection pool ayarı:

ConnectionProvider cp = ConnectionProvider.builder("fixed").maxConnections(100).pendingAcquireMaxCount(500).build();
HttpClient client = HttpClient.create(cp).option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000);
WebClient web = WebClient.builder().clientConnector(new ReactorClientHttpConnector(client)).build();
Tomcat thread pool veya Netty ayarı için application.yml örneği (somut parametreler):
server:
  tomcat:
    threads:
      max: 200
      min-spare: 10
spring:
  datasource:
    hikari:
      maximum-pool-size: 50
      leak-detection-threshold: 2000
Sorun tespiti için komutlar: jstack > threaddump.txt ve async-profiler ile blokaj profili:
./profiler.sh -d 30 -e wall -o collapsed -f block.collapsed 
# collapsed çıktısını flamegraph scriptine verin
./flamegraph.pl block.collapsed > block.svg
Ölçüm: istek gecikme p95/p99 değerlerini Prometheus sorguları ile baseline ve iyileştirme sonrası karşılaştırın. Hangi metrikler? http_server_requests p99, jvm_memory_used_bytes ve HikariCP activeConnections.

Model Context Protocol ve Spring AI çağrıları: payload, timeout ve MCP sınamaları

Model çağrılarında en sık ihmal edilen iki konu: context payload boyutu ve çağrı izolasyonu. 'spring ai' ve 'spring mcp' (model context protocol) ile entegreken somut uygulama: WebClient isteklerine Micrometer timer ve OpenTelemetry span ekleyin, ayrıca Resilience4j timeout/bulkhead ile koruyun. Örnek kod:

Timer.Sample sample = Timer.start(meterRegistry);
try {
  String resp = webClient.post()
    .uri("/model/infer")
    .bodyValue(payload)
    .retrieve()
    .bodyToMono(String.class)
    .timeout(Duration.ofSeconds(5))
    .block();
} finally {
  sample.stop(meterRegistry.timer("model.call.latency", "model", "my-model"));
}
MCP dikkate alın: context boyutu arttıkça network serialization ve JSON parse maliyeti linear artar; p95 gecikme artışı için payload byte cinsinden izleyin. Prometheus ile p95/p99 ölçümü yapın:
histogram_quantile(0.99, sum(rate(model_call_latency_seconds_bucket[5m])) by (le))
Ayrıca, model çağrılarının hatalı/uzun süreli cevapları için circuit breaker ekleyin:
resilience4j.circuitbreaker.instances.model.max-failures=5
resilience4j.circuitbreaker.instances.model.wait-duration-in-open-state=30s
Bu bölüm, spring ai ve model context protocol üzerinden çalışan servislerde payload küçültme (tokenize/trim), stream veya chunk bazlı gönderim ve timeout+bulkhead kombinasyonunun neden zorunlu olduğunu teknik olarak açıklar.

Sık Sorulan Sorular

spring boot eğitimi sırasında JVM profiling nasıl öğretilir, hangi araçlarla başlanmalı?

Öğretim pratik olmalı: öğrencilere önce JFR ile 60s baseline alma, ardından async-profiler ile CPU flamegraph üretme pratiği yaptırın. Komut örnekleri: jcmd JFR.start name=base settings=profile duration=60s filename=base.jfr ve ./profiler.sh -d 60 -e cpu -f baseline.svg . Sonra JFR'i Java Mission Control ile açıp allocation ve GC eventlerini gösterin.

hibernate orm batch save nasıl doğru yapılandırılır ve neden ID stratejisi önemli?

Doğru ayarlar: spring.jpa.properties.hibernate.jdbc.batch_size ve order_inserts/order_updates. Ancak IDENTITY stratejisi JDBC batch'i devre dışı bırakır, bu yüzden SEQUENCE veya UUID tercih edin. Örnek config: spring.jpa.properties.hibernate.jdbc.batch_size=50. Ayrıca saveAll sonrası entityManager.flush() ve clear() ile memory kullanımını kontrol edin.

java microservices için thread pool ve connection pool hangi metriklerle izlenmeli?

Ölçümler somut olmalı: Tomcat/Netty activeThreads, Reactor Netty activeConnection sayısı, HikariCP activeConnections, http request p95/p99 ve jvm threads blocked. Araçlar: Prometheus + Grafana, jstack, async-profiler (wall event). Komut örnekleri: jstack > dump.txt ve ./profiler.sh -d 30 -e wall -f blocks.svg .

spring ai ve model context protocol ile çalışan uygulamalarda gecikmeyi nasıl azaltırım?

Payload küçültme (trim, tokenize), chunked streaming, ve MCP frame'lerini sınırlama ilk adımdır. Teknik uygulama: WebClient ile timeout ve bulkhead, Micrometer ile latency ölçümü, Prometheus histogram_quantile ile p95/p99 takip. Örnek: .timeout(Duration.ofSeconds(5)) ve Resilience4j circuit breaker konfigürasyonu.

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

📚 Bu konuyu Opendart Akademi'de öğrenin: Spring Boot eğitimi · Java Spring Boot eğitimi hakkında · YouTube video dersleri · Abone ol