# Java Sistemlerinde Çalışma Zamanı Optimizasyonu

> Java performansında ortalama gecikme tek başına yeterli değildir; kuyruklar, bellek ayırma rate, GC, kilit çekişmesi ve aşağı akış gecikmeleri özellikle P99 ve üzerindeki davranışı belirler.

- Author: Muhammet Ali Köker
- Language: tr
- Canonical: https://alikoker.com.tr/java-sistemlerinde-calisma-zamani-optimizasyonu
- Translation: https://alikoker.com.tr/en/runtime-optimization-in-java-systems
- Published: 2026-08-05T18:00:00+03:00
- Modified: 2026-09-08T02:30:00+03:00
- Verified: 2026-09-08T02:30:00+03:00
- Type: article

Yüksek trafikli Java servislerinde performans sorunu çoğu zaman ortalamada değil gecikme dağılımının sağ kuyruğunda görünür. Ağ beklemeleri, kuyruk birikmesi, kilit çekişmesi, çöp toplama ve aşağı akış servislerindeki değişkenlik P99 ve üzerindeki istekleri büyütür. Bir saat içinde bir milyon istek işleniyorsa P99 bölgesi yaklaşık on bin isteği temsil eder; bu nedenle ortalama ve medyan tek başına operasyonel resmi vermez.

Çok katmanlı çağrı zincirlerinde uçtan uca gecikme, yalnız tek bir servisin yüzdelik değerinden türetilemez. Beş bağımsız bileşenin her birinde belirli bir gecikme eşiğinin aşılma olasılığı yüzde 1 ise çağrı zincirinde en az bir bileşenin bu eşiği aşma olasılığı `1 - 0.99^5` hesabıyla yaklaşık yüzde 4,9 olur. Bu hesap bağımsızlık varsayımına dayanır. Gerçek sistemlerde ortak veritabanı, aynı ağ yolu, paylaşılan işlemci veya eşzamanlı [GC](/wiki/garbage-collection) faaliyeti gibi nedenlerle gecikmeler korelasyon gösterebilir. Böyle bir durumda basit olasılık hesabı iyimser kalabilir.

Kuyruk gecikmesinin doğru ölçülmesi, yük üretecinin trafik modeline doğrudan bağlıdır. Kapalı döngü testinde sanal kullanıcı bir isteğin yanıtını aldıktan sonra yenisini gönderir. Sunucu yavaşladığında istemci de istek üretimini azaltır. Böylece üretim ortamında kuyruğu büyütecek olan sabit veya dışsal trafik baskısı, test sırasında kendiliğinden düşer.

Bu ölçüm hatası koordineli ihmal olarak adlandırılır. Örneğin sunucu bir saniye boyunca yanıt veremediğinde kapalı döngü istemciler bu sürede yeni istek oluşturmaz. [Histogram](/wiki/histogram) yalnız gönderilmiş istekleri ölçtüğü için duraksamanın neden olacağı gerçek kuyruk görünmez. Açık döngü veya sabit varış hızlı testlerde ise istek üretim zamanı sunucunun yanıt süresinden ayrılır. Sistem kapasitesini aştığında kuyruk, reddetme ve zaman aşımı davranışları gözlenebilir hale gelir. Üretim trafiği dış kaynaklı ve sunucu hızından bağımsızsa kapasite testinin de bu özelliği yansıtması gerekir.

Yüksek trafikli Java servislerinde gecikme dağılımı, bellek ayırma, GC, kilit çekişmesi ve aşağı akış beklemeleri birlikte ölçülmelidir. Ortalama süre sağlıklı görünürken P99 kuyruğunun büyüyebilmesi, optimizasyon kararlarının tekil mikro ölçümler yerine uçtan uca kuyruk ve kaynak davranışına dayanmasını gerektirir.

## Kuyruk Teorisi ve Havuz Boyutlandırma

[Little Yasası](/wiki/little-s-law), kararlı durumdaki bir sistem için ortalama eşzamanlılık, ortalama varış hızı ve sistemde geçirilen ortalama süre arasındaki ilişkiyi açıklar:

`L = λ × W`

Buradaki `L`, sistemde aynı anda bulunan ortalama iş veya istek sayısıdır. Yunan alfabesindeki lambda işaretiyle gösterilen `λ`, birim zamandaki ortalama varış sayısını ifade eder. `W` ise bir isteğin sisteme girişinden çıkışına kadar geçen ortalama süredir.

Örneğin saniyede ortalama 2.000 istek alan ve ortalama yanıt süresi 40 milisaniye olan bir sistem için:

`L = 2.000 × 0,040 = 80`

Bu sonuç, kararlı durumda ortalama 80 isteğin işlemde veya beklemede olacağını gösterir. Ancak bu değer doğrudan iş parçacığı ya da [bağlantı havuzu](/wiki/connection-pool) boyutu değildir. Little Yasası ortalamalar arasındaki ilişkiyi verir; P99 gecikmesini, ani trafik artışlarını, servis süresi dağılımını ve kaynak çekişmesini tek başına modellemez.

Veritabanı bağlantı havuzu, uygulamadaki eşzamanlı istek sayısına göre sınırsız büyütülmemelidir. Bağlantılar, veritabanı tarafında oturum, bellek, kilit, yürütme bağlamı ve işlemci zamanı tüketir. Uygulama yüzlerce bağlantı açsa bile veritabanı yalnız sınırlı sayıda sorguyu verimli biçimde yürütebilir. Bu sınır aşıldığında işlem hacmi artmayabilir; bağlam değiştirme, kilit rekabeti, önbellek kaybı ve sorgu kuyruğu büyüyebilir.

`((çekirdek sayısı × 2) + etkin disk sayısı)` biçimindeki formül, bazı geleneksel veritabanı iş yükleri için kullanılan tarihsel bir başlangıç sezgisidir. SSD, uzak veritabanı, çok soketli işlemci, karma sorgu profili ve yüksek ağ gecikmesi bulunan sistemlerde evrensel bir optimum vermez. Havuz boyutu kontrollü yük testleriyle belirlenmeli; veritabanı CPU kullanımı, aktif oturum sayısı, sorgu bekleme olayları, kilit süreleri ve uygulama kuyruğu birlikte izlenmelidir.

Havuz dolduğunda sınırsız bekleme yerine zaman sınırı olan bir kabul politikası uygulanmalıdır. Sınırlı kuyruk, fast-fail, geri basınç veya yük azaltma mekanizmalarından biri sistem sözleşmesine göre seçilmelidir. `connectionTimeout`, işlem zaman aşımı ve sürücü seviyesindeki ağ zaman aşımı birbirleriyle uyumlu olmalıdır. `maxLifetime` gibi yaşam süresi ayarları da veritabanı, güvenlik duvarı ve yük dengeleyici tarafından uygulanan bağlantı sürelerinden kısa seçilerek kullanım sırasında sessizce kapanan bağlantıların etkisi azaltılmalıdır.

## İş Parçacığı Modelleri

Platform iş parçacıkları işletim sistemi tarafından zamanlanır ve her biri için yerel yığın ile çekirdek kaynakları ayrılır. Çok sayıda eşzamanlı ve çoğunlukla bekleyen bağlantının platform iş parçacıklarıyla bire bir eşlenmesi, bellek tüketimini ve zamanlayıcı maliyetini büyütür.

Sanal iş parçacıkları, Java çalışma zamanı tarafından yönetilen hafif iş parçacıklarıdır. Uygulama kodu yine sıralı ve bloklayıcı biçimde yazılabilir; JVM, sanal iş parçacıklarını daha az sayıdaki taşıyıcı platform iş parçacığı üzerinde çalıştırır. Sanal iş parçacığı desteklenen bir bloklayıcı I/O işleminde beklemeye geçtiğinde taşıyıcıdan ayrılabilir. Çağrı yığını JVM tarafından yönetilen öbek tabanlı stack chunk yapılarında tutulur ve iş devam edebildiğinde sanal iş parçacığı uygun bir taşıyıcıya yeniden bağlanır.

Bu model CPU kapasitesini artırmaz. CPU-bound bir iş yükünde çalıştırılabilir görev sayısını çekirdek sayısının çok üzerine çıkarmak işlem hacmi sağlamaz ve zamanlayıcı baskısını artırabilir. Sanal iş parçacıklarının temel yararı, çok sayıda eşzamanlı görevin önemli bölümünün ağ, dosya veya veritabanı I/O işlemlerinde beklediği sistemlerde ortaya çıkar.

Sanal iş parçacıkları bağlantı havuzu gibi kıt kaynak sınırlarını da ortadan kaldırmaz. Yüz bin sanal iş parçacığı oluşturulabilmesi, yüz bin eşzamanlı veritabanı sorgusunun güvenli olduğu anlamına gelmez. Kıt kaynaklar semaphore, bağlantı havuzu, sınırlı kuyruk veya benzeri kabul denetimleriyle ayrıca sınırlandırılmalıdır.

Java 21 döneminde `synchronized` blok içinde gerçekleşen bazı beklemeler sanal iş parçacığını taşıyıcıya sabitleyebiliyordu. Bu nedenle eski rehberlerde `ReentrantLock` kullanımına yönelik genel öneriler yer aldı. Java 24 ile birlikte nesne monitörlerinden kaynaklanan bu sabitlenme büyük ölçüde giderildi. Bu sürümlerde yalnız sanal iş parçacığı kullanıldığı için `synchronized` kodu topluca `ReentrantLock` ile değiştirmek doğru bir optimizasyon yaklaşımı değildir. Kilit türü; adillik, kesilebilir bekleme, zaman sınırlı edinme, condition desteği ve ölçülen çekişme davranışına göre seçilmelidir.

Uzun süreli CPU hesaplamasını kilit altında yürütmek, sanal veya platform iş parçacığı ayrımı olmadan kritik bölgeyi büyütür. I/O çağrısını kilit altında tutmak da başka görevlerin ilerlemesini engelleyebilir. Öncelik kilit türünü değiştirmek değil, paylaşılan değiştirilebilir durumu azaltmak ve kritik bölgeyi mümkün olan en küçük kapsama indirmektir.

Yüksek çekişmeli istatistik sayaçlarında `AtomicLong` üzerindeki tek bellek konumu darboğaz oluşturabilir. `LongAdder`, güncellemeleri birden fazla hücreye dağıtarak yazma çekişmesini azaltır. Buna karşılık `sum()` sonucu eşzamanlı güncellemeler sırasında atomik bir anlık görüntü değildir. Kesin ve doğrusal tutarlı sayaç gereken iş kurallarında `LongAdder` yerine uygun atomik veya kilitli yapı kullanılmalıdır.

## Çöp Toplayıcı Seçimi

Çöp toplayıcı seçimi işlem hacmi, duraksama süresi, CPU maliyeti ve bellek rezervi arasında ölçülmesi gereken bir ödünleşimdir. Bu değişkenler arasında her ortamda geçerli, kesin bir "üçünden ikisini seçme" kuralı yoktur. Uygun seçim iş yükünün tahsis hızı, canlı veri boyutu, öbek kapasitesi, gecikme hedefi ve işlemci rezerviyle belirlenir.

[G1](/wiki/g1-gc), öbek alanını region adı verilen bölgelere ayırır ve tahmini duraksama hedeflerine göre tahliye edilecek bölgeleri seçer. Genel amaçlı sunucu uygulamalarında işlem hacmi ile makul duraksama süreleri arasında dengeli bir başlangıç noktası sunar. Bununla birlikte `MaxGCPauseMillis` kesin bir üst sınır değildir. Yüksek bellek ayırma rate, humongous nesneler, yetersiz öbek rezervi veya geciken concurrent marking döngüsü hedefin aşılmasına neden olabilir.

[ZGC](/wiki/zgc), işaretleme, yeniden yerleştirme ve referans işleme çalışmalarının büyük bölümünü uygulama iş parçacıklarıyla eşzamanlı yürütür. Bu nedenle büyük öbek alanlarında düşük duraksama hedefleyen sistemlere uygundur. Düşük duraksama sürelerinin öbek boyutuyla doğrudan büyümemesi, duraksamaların her koşulda sabit veya garantili olduğu anlamına gelmez. Kök kümesinin büyüklüğü, işletim sistemi zamanlaması, bellek basıncı ve yetersiz CPU rezervi yine ölçülebilir sapmalar oluşturabilir.

Java 24 ve sonraki sürümlerde ZGC yalnız generational yapıda çalışır. Nesneleri genç ve yaşlı kuşaklarda izlemek, kısa ömürlü nesnelerin daha düşük maliyetle toplanmasını hedefler. Buna rağmen ZGC kullanmak bellek ayırma rate sorununu ortadan kaldırmaz. Uygulama GC iş parçacıklarının yetişemeyeceği hızda nesne üretiyorsa öbek genişler, CPU tüketimi artar ve sonunda bellek ayırma stall veya bellek yetersizliği oluşabilir.

Parallel GC, duraksama sırasında çok sayıda GC iş parçacığı kullanarak toplam işlem hacmi değerini yükseltmeye odaklanır. Uzun duraksamaların kabul edilebildiği batch işlemlerinde uygun olabilir. [Gecikme](/wiki/latency) duyarlı çevrim içi servislerde ise aynı davranış P99 ve P99.9 değerlerini bozabilir. Toplayıcı adı üzerinden karar vermek yerine aynı üretim benzeri yük altında GC süresi, CPU kullanımı, bellek ayırma rate, promotion rate, canlı veri boyutu ve uç gecikmeler birlikte karşılaştırılmalıdır.

Nesne tahsisini azaltma çalışmaları yalnız `new` kullanımını kaldırmaya indirgenmemelidir. Kısa ömürlü küçük nesnelerin [TLAB](/wiki/thread-local-allocation-buffer) üzerinden tahsisi oldukça ucuz olabilir. Asıl maliyet; yüksek tahsis hacmi, büyük nesneler, gereksiz kopyalama, uzun yaşayan ara veri yapıları ve eski kuşağa taşınan nesnelerde ortaya çıkar. Optimizasyon, bellek ayırma profili ölçülmeden nesne havuzları eklemek yerine en yüksek toplam byte tahsisine neden olan yolları hedeflemelidir.

## Profilleme ve Safepoint Sapması

JVM performans sorunları yalnız CPU örnekleriyle teşhis edilemez. CPU zamanı, duvar saati, kilit bekleme süresi, dosya ve ağ I/O olayları, nesne tahsisi, GC faaliyeti ve iş parçacığı park süreleri farklı veri kümeleridir. Bir metodun duvar saatinde uzun görünmesi, işlemciyi yoğun kullandığı anlamına gelmeyebilir; süre aşağı akış servisini beklerken geçmiş olabilir.

[Safepoint](/wiki/safepoint) tabanlı örnekleyiciler, iş parçacığı yığınını yalnız JVM tarafından güvenli kabul edilen noktalarda gözleyebilir. Derlenmiş kodun bazı bölgeleri diğerlerinden daha seyrek safepoint içeriyorsa örneklerin dağılımı gerçek CPU kullanımından sapabilir. Bu olgu safepoint bias olarak bilinir. Uzun döngüler veya yoğun derlenmiş sıcak yollar bazı profillerde olduğundan az, döngü sonrasındaki kod ise olduğundan fazla görünebilir.

`async-profiler`, desteklenen platformlarda `perf_events` ve asenkron stack walking mekanizmalarını kullanarak CPU, bellek ayırma, lock ve wall-clock profilleri üretebilir. [Java Flight Recorder](/wiki/java-flight-recorder) ise JVM içinden düşük ek yükle uzun süreli olay kaydı sağlar. Java 25 sürümünde JFR örneklemesinde güvenliği artırırken safepoint sapmasını azaltmaya yönelik cooperative sampling geliştirmeleri de bulunmaktadır. Araç seçimi tek başına yeterli değildir; örnekleme kaynağı, frekansı, kayıt süresi ve iş yükü açıkça belirlenmelidir.

Alev grafiğinde yatay genişlik, seçilen örnek türündeki örnek sayısını veya ağırlığını temsil eder. CPU alev grafiğinde bu genişlik yaklaşık CPU tüketimiyle ilişkilidir; bellek ayırma grafiğinde ise tahsis edilen byte veya nesne sayısını gösterebilir. Yatay konum zaman sırasını göstermez. Dikey eksen çağrı zincirini gösterir ve üstteki geniş çerçeveler genellikle doğrudan maliyet üreten leaf veya near-leaf metotlara işaret eder.

JSON serileştirme, karakter kodlama, geçici `String` üretimi, koleksiyon kopyalama ve regex işlemleri CPU profilinde orta düzeyde görünürken bellek ayırma profilinde baskın olabilir. Bu tahsisler GC sıklığını artırarak gecikmeyi dolaylı biçimde bozar. Bu nedenle CPU ve bellek ayırma profilleri aynı yük penceresi için birlikte değerlendirilmelidir.

## JIT ve Sıcak Yol Davranışı

HotSpot, bytecode kodunu önce yorumlayabilir, ardından çalışma zamanı profiline göre farklı derleme katmanlarında makine koduna dönüştürebilir. C1 hızlı derleme ve profil toplama görevlerinde öne çıkarken C2 daha pahalı fakat daha gelişmiş optimizasyonlar uygular. Uygulama henüz yeterli profil toplamamışken görülen performans ile kararlı durum performansı aynı değildir.

Inlining, yalnız metot çağrı maliyetini azaltmaz; sabit yayılımı, devirtualization ve dead-code elimination gibi sonraki optimizasyonların önünü açar. Ancak her metot içlenmez. Metot boyutu, çağrı alanının polimorfizmi, derleyici bütçeleri ve çalışma zamanı profili kararı etkiler. Bir arayüz çağrısında çalışma zamanı sürekli tek somut tür görürse çağrı monomorfik kabul edilerek doğrudanlaştırılabilir. Daha sonra yeni türler görülürse JVM spekülatif kodu geçersiz kılar ve yeniden derleme yapabilir.

[Kaçış analizi](/wiki/escape-analysis), oluşturulan nesnenin metot veya iş parçacığı dışına çıkıp çıkmadığını inceleyen bir derleyici analizidir. HotSpot C2, uygun nesnelerde [scalar replacement](/wiki/scalar-replacement) uygulayarak nesneyi alanlarına ayırabilir ve gerçek öbek tahsisini tamamen kaldırabilir. Bu davranış sıkça "stack bellek ayırma" olarak anlatılsa da HotSpot genel durumda nesneyi bütün halinde yerel stack alanına taşımaz. Tahsisin ortadan kaldırılması ve alanların register ya da derleyici tarafından yönetilen geçici değerler olarak ele alınması daha doğru açıklamadır.

Spekülatif varsayım bozulduğunda JVM derlenmiş çerçeveyi deoptimize edebilir. Gerekli nesneler yeniden materialize edilir ve yürütme daha düşük derleme katmanında devam edebilir. Bu olaylar yoğunlaşırsa gecikme dağılımında geçici sıçramalar görülebilir. [JIT](/wiki/just-in-time-compilation) logları, JFR compilation olayları ve code önbellek metrikleri olmadan yalnız kaynak koda bakarak bu davranışı kesin biçimde belirlemek mümkün değildir.

Mikro başarım ölçümü sonuçları JIT tarafından dead-code elimination, constant folding veya aşırı inlining nedeniyle yanıltılabilir. Java Microbenchmark Harness, warm-up, fork, sonuç tüketimi ve ölçüm iterasyonlarını denetlemek için kullanılmalıdır. Buna rağmen mikro başarım ölçümü üretim sistemindeki kuyruk, ağ, GC ve veritabanı etkileşimini temsil etmez. Yerel optimizasyonun uçtan uca gecikmeye etkisi ayrıca ölçülmelidir.

[GraalVM](/wiki/graalvm) [Native Image](/wiki/native-image), uygulamayı önceden makine koduna derleyerek JVM başlatma ve JIT ısınma maliyetini azaltabilir. Ancak yansıma tamamen ortadan kalkmaz; dinamik erişimlerin önemli bölümü derleme sırasında belirlenmeli veya üstveri ile tanımlanmalıdır. Native Image çalışma zamanı HotSpot C2 ile aynı dinamik optimizasyon modeline sahip değildir, fakat önceden toplanmış iş yükü profilleriyle PGO uygulanabilir. Bu nedenle seçim yalnız başlangıç süresine göre değil; işlem hacmi, bellek kullanımı, derleme karmaşıklığı, gözlemlenebilirlik ve dinamik özellik gereksinimleriyle birlikte yapılmalıdır.

## Uçtan Uca Optimizasyon

Serileştirme formatı, sıkıştırma ve zero-copy mekanizmaları her sistem için otomatik optimizasyon değildir. JSON yerine Protocol Buffers gibi ikili bir formata geçmek mesaj boyutunu ve ayrıştırma maliyetini azaltabilir; ancak şema yönetimi, hata ayıklama ve istemci uyumluluğu maliyeti getirir. FlatBuffers gibi formatlar belirli erişim örüntülerinde kopyalamayı azaltabilir, fakat küçük mesajlarda veya basit iş akışlarında toplam karmaşıklık kazancı aşabilir.

Sıkıştırma kararı yalnız veri yükü boyutuna sabit bir eşik koyularak verilmemelidir. İçeriğin sıkıştırılabilirliği, ağ bant genişliği, işlemci rezervi, gecikme hedefi ve istemci desteği birlikte ölçülmelidir. Zstandard, Brotli veya gzip farklı sıkıştırma oranı ve CPU maliyeti profillerine sahiptir. Önceden sıkıştırılmış ses, görüntü ve video verisini tekrar sıkıştırmak çoğu durumda yarar sağlamaz.

`FileChannel.transferTo` gibi zero-copy yolları, dosya içeriğinin kullanıcı uzayına kopyalanmasını azaltabilir. Bunun gerçek davranışı işletim sistemi, dosya sistemi, TLS kullanımı ve JDK uygulamasına bağlıdır. TLS sonlandırma veya uygulama seviyesinde veri dönüşümü gerektiğinde tam zero-copy yolu kullanılamayabilir.

Çalışma zamanı optimizasyonu tek bir JVM bayrağı, GC seçimi veya kod kalıbıyla tamamlanmaz. Önce [hizmet seviyesi hedefi](/wiki/service-level-objective) tanımlanmalı; ardından açık döngü yük altında işlem hacmi, P50, P95, P99 ve P99.9 değerleri ölçülmelidir. Aynı zaman aralığında kuyruk uzunluğu, aktif bağlantı, reddedilen iş, CPU kullanımı, bellek ayırma rate, GC olayları, kilit beklemeleri ve aşağı akış gecikmeleri kaydedilmelidir.

Değişiklikler tek tek uygulanmalı ve aynı trafik profili altında karşılaştırılmalıdır. Ortalama değeri iyileştirirken kuyruk gecikmesini bozan veya işlem hacmi değerini artırırken hata oranını yükselten değişiklikler başarılı kabul edilmemelidir. Java çalışma zamanı optimizasyonu, varsayımlara dayalı bayrak ayarlama işi değil; ölçülebilir hipotezler, kontrollü deneyler ve üretim geri bildirimleriyle yürütülen sistem mühendisliği sürecidir.

## Optimizasyonun Sınırı: Ortalama Değil Dağılım

JVM üzerinde bir optimizasyonu yalnız ortalama süreyle değerlendirmek, GC duraklaması, JIT ısınması, lock contention veya CPU scheduling kaynaklı uç değerleri gizleyebilir. Üretim yükünde önemli olan çoğu zaman en hızlı örnek değil, yoğunluk yükseldiğinde gecikme dağılımının nasıl şekil değiştirdiğidir.

Bu nedenle benchmark sonucu ile servis davranışı arasında [P99 Latency](/wiki/p99-latency), [Queueing Delay](/wiki/queueing-delay), [CPU Affinity](/wiki/cpu-affinity) ve [False Sharing](/wiki/false-sharing) gibi kavramlarla köprü kurmak gerekir. Aynı değişiklik throughput'u artırırken tail latency'yi kötüleştirebilir; optimizasyon kararı iş yükünün gerçek SLO'suna göre verilmelidir.

## Kaynakça

- John D. C. Little. (1961). A Proof for the Queuing Formula: L = λW. Operations Research, 9(3), 383-387. [doi:10.1287/opre.9.3.383](https://doi.org/10.1287/opre.9.3.383)

- Patricio Chilano Mateo; Alan Bateman. (2024). JEP 491: Synchronize Virtual Threads without Pinning. OpenJDK. [URL](https://openjdk.org/jeps/491)

- Ron Pressler; Alan Bateman. (2023). JEP 444: [Virtual Threads](/wiki/virtual-thread). OpenJDK. [URL](https://openjdk.org/jeps/444)

- Stefan Karlsson. (2023). JEP 439: Generational ZGC. OpenJDK. [URL](https://openjdk.org/jeps/439)

## Bu Çalışmaya Atıf

Köker, M. A. (2026). Java Sistemlerinde Çalışma Zamanı Optimizasyonu. alikoker.com.tr. https://alikoker.com.tr/java-sistemlerinde-calisma-zamani-optimizasyonu

- BibTeX: https://alikoker.com.tr/java-sistemlerinde-calisma-zamani-optimizasyonu.bib
- RIS: https://alikoker.com.tr/java-sistemlerinde-calisma-zamani-optimizasyonu.ris
- CSL-JSON: https://alikoker.com.tr/java-sistemlerinde-calisma-zamani-optimizasyonu.csl.json
