# Go'da GC baskısını sync.Pool ve escape analizi ile nasıl azaltırım?

> Duraklamaların ardındaki düşman tahsis hızıdır: pprof ile en çok tahsis edeni öldürün, hot path'i sync.Pool ile havuzlayın, GOGC ayarını en sona bırakın.

- Soruldu: 2026-05-14
- Yanıtlandı: 2026-05-17
- Soran: Arda
- Etiketler: performans, go
- Kaynak: https://www.muhammetsafak.com.tr/sor-bakalim/go-da-gc-baskisini-sync-pool-ve-escape-analizi-ile-azaltmak/
- Dil: tr-TR
- Yazar: Muhammet Şafak

---
**Soru:** Go ile yazdığımız bir veri ingest servisi saniyede 50.000 küçük JSON paketi alıp işleyip veritabanına yazıyor. Yoğun yükte GC devreye girince CPU tavan yapıyor ve "Stop-the-World" duraklamaları API yanıt sürelerinde spike yaratıyor.

Bellekteki nesne tahsisini azaltmak için `sync.Pool` ve escape analysis'i bu senaryoda nasıl uygularım?


Kısa cevap: GC duraklamaları bir semptom; asıl düşman **tahsis hızı** (allocation rate). Önce tahsisleri kesin, GC zaten rahatlar.

Yaşadığınız spike'lar GC'nin "kötü" olmasından değil — saniyede 50k paket, saniyede yüz binlerce kısa ömürlü nesne demek; GC bu çöpü toplamak için sürekli ve agresif çalışıyor, Stop-the-World duraklamaları da yanıt süresine yansıyor. Çözüm GC'yi kapatmak değil, ona daha az iş vermek.

1. **Hot path'te `sync.Pool` ile yeniden kullanın.** Ingest yolundaki kısa ömürlü buffer'ları, struct'ları ve decoder'ları her pakette `new`'lemek yerine havuzdan al-geri verin. Kritik detay: `Put` etmeden önce nesneyi **resetleyin** ki eski veri sızmasın. Bu, en sıcak yoldaki tahsis basıncını dramatik düşürür.
2. **Tahsisi kökten azaltın.** Slice/map'leri biliyorsanız baştan `make([]T, 0, n)` ile boyutlandırın; `bytes.Buffer` ve JSON decoder'ı tekrar kullanın; gereksiz `[]byte`↔`string` kopyalarından kaçının. Tek bir kopya 50k kez çarpınca dağ olur.
3. **Escape analizini çalıştırın, stack'te tutun.** `go build -gcflags=-m` çıktısı size hangi değerin heap'e "kaçtığını" (escape) söyler. Bir pointer'ı fonksiyondan dışarı kaçırmak, bir değeri interface'e koymak çoğu kaçışın nedenidir; bunları stack'te tutarsanız GC o nesneyi hiç görmez — en ucuz tahsis, hiç yapılmayan tahsistir.
4. **Kör gitmeyin: `pprof` ile önce/sonra ölçün.** `pprof`'un `alloc_space` profili size en çok tahsis eden satırı söyler; `GODEBUG=gctrace=1` ile GC sıklığını ve duraklama süresini izleyin. Değişikliği uygulamadan önce ve sonra aynı yükle ölçün — "iyileştirdim" diye düşündüğünüz şey çoğu zaman ölçünce yerinde sayıyordur.
5. **Pooling'in tuzağına dikkat.** Başka heap verisine pointer tutan nesneleri havuzlamak ters tepebilir: havuz o nesneyi canlı tuttuğu için işaret ettiği büyük graf da serbest kalmaz, bellek beklediğinden çok şişer. Havuzda yalnızca düz, kendi içinde kapalı buffer/struct'ları tutun.

**Sonuç:** Sıralama net: **profilleyin → en çok tahsis edeni öldürün → hayatta kalanları havuzlayın → sonra `GOGC`/`GOMEMLIMIT` ayarlayın.** `GOGC` ve soft memory limit (`GOMEMLIMIT`) GC'yi seyrekleştirir ama saniyede 50k tahsis yapan bir yolu düzeltmez; onlar son ince ayar, ilk hamle değil. Asıl kazanç, GC'ye en sıcak yolda hiç çöp üretmemekten gelir.

## İlgili Yazılar

- [Go'da eşzamanlılık: goroutine ve channel pratiği](/blog/goda-eszamanlilik-goroutine-ve-channel-pratigi/) — Blog
- [Autoscaling scale-in sırasında SIGTERM ile graceful shutdown'ı nasıl sağlarım?](https://www.muhammetsafak.com.tr/sor-bakalim/autoscaling-sirasinda-sigterm-ile-graceful-shutdown/) — Sor Bakalım
- [Birbirine bağımlı birden fazla işi, ilk hata hepsini iptal edecek şekilde errgroup ile mi yürütmeliyim?](https://www.muhammetsafak.com.tr/sor-bakalim/birbirine-bagimli-birden-fazla-isi-ilk-hata-hepsini-iptal-edecek-sekilde/) — Sor Bakalım
- [İşlediğim satırları aynı anda güncellerken chunk yerine chunkById mı kullanmalıyım?](https://www.muhammetsafak.com.tr/sor-bakalim/isledigim-satirlari-ayni-anda-guncellerken-chunk-yerine-chunkbyid-mi-kullanmaliyim/) — Sor Bakalım
