# 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://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.

## Kısa cevap

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. Bu yolun eşzamanlılık tarafını [Go'da goroutine ve channel pratiği yazısında](/tr/blog/goda-eszamanlilik-goroutine-ve-channel-pratigi/) ele almıştım.

## Neden

1. **Duraklamanın kaynağı tahsis sıklığıdır.** GC ne kadar çöp üretirseniz o kadar sık koşar; ayarı değiştirmek üretimi değiştirmez.

2. **En ucuz tahsis, hiç yapılmayandır.** Bir değeri stack'te tutabiliyorsanız GC o nesneyi hiç görmez — havuzlamaktan da ucuzdur.

3. **Pooling'in bir ters tepme riski var.** Başka heap verisine pointer tutan bir nesneyi havuzlarsanız, havuz onu canlı tuttuğu için işaret ettiği büyük graf da serbest kalmaz; bellek beklediğinizden çok şişer.

## Ne yapmalı

1. **Kör gitmeyin: `pprof` ile önce/sonra ölçün.** `alloc_space` profili en çok tahsis eden satırı söyler; `GODEBUG=gctrace=1` GC sıklığını ve duraklama süresini gösterir. Aynı yükle önce ve sonra ölçün.

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ı hangi değerin heap'e kaçtığını söyler. Yaygın nedenler: bir pointer'ı fonksiyondan dışarı döndürmek, boyutu dinamik slice'lar ve zaten kaçmış değerlere pointer yazmak.

4. **Hot path'te `sync.Pool` ile yeniden kullanın.** Ingest yolundaki kısa ömürlü buffer'ları, struct'ları ve decoder'ları havuzdan al-geri verin. `Put` etmeden önce nesneyi **resetleyin** ki eski veri sızmasın. Havuzda yalnızca düz, kendi içinde kapalı buffer/struct'ları tutun.

5. **`GOGC`/`GOMEMLIMIT` ayarını en sona bırakın.** GC'yi seyrekleştirirler ama saniyede 50k tahsis yapan bir yolu düzeltmezler; son ince ayar, ilk hamle değil.

**Sonuç:** Sıralama net: **profilleyin → en çok tahsis edeni öldürün → hayatta kalanları havuzlayın → sonra `GOGC`/`GOMEMLIMIT` ayarlayın.** 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](/tr/blog/goda-eszamanlilik-goroutine-ve-channel-pratigi/) — Blog
- [Autoscaling scale-in sırasında SIGTERM ile graceful shutdown'ı nasıl sağlarım?](https://muhammetsafak.com/tr/sor-bakalim/autoscaling-sirasinda-sigterm-ile-graceful-shutdown/) — Sor Bakalım
- [Kanal tampon boyutu seçimi backpressure sorunlarını gizler mi, nasıl karar vermeliyim?](https://muhammetsafak.com/tr/sor-bakalim/kanal-tampon-boyutu-backpressure-sorunlarini-gizler-karar-vermeliyim/) — Sor Bakalım
- [Container'daki Go worker'ım SIGTERM almıyor ve graceful kapanmıyor, bu bir PID 1 sorunu mu?](https://muhammetsafak.com/tr/sor-bakalim/containerdaki-go-workerim-sigterm-almiyor-pid-1-sorunu/) — Sor Bakalım
