Go'da string ve []byte arasında dönüşüm genellikle yeni bellek ayrılmasına (allocation) neden olur, çünkü stringler değiştirilemez (immutable), byte dilimleri ise değiştirilebilir (mutable). Bazı iç optimizasyonlar (escape analysis) istisna teşkil eder, ancak bunlar garanti edilmez ve Go sürümleri arasında değişebilir.
Doğrudan dönüşüm:
[]byte(s string): her zaman yeni bir byte dizisi tahsis eder ve verileri kopyalar.string(b []byte): ayrıca her zaman byte dizisinden yeni bir string'e verileri kopyalar.Zero allocation — gereksiz ayrımaları önlediğimiz bir yaklaşımdır. Go standart kütüphanesinde, kopyalama yapmadan aynı verilere referans veren unsafe paketi aracılığıyla güvenli olmayan bir dönüşüm vardır. Bunu sadece risklerin tam olarak anlaşıldığı durumlarda kullanmalısınız.
import ( "reflect" "unsafe" ) func BytesToString(b []byte) string { return *(*string)(unsafe.Pointer(&b)) } func StringToBytes(s string) []byte { sh := (*reflect.StringHeader)(unsafe.Pointer(&s)) bh := reflect.SliceHeader{ Data: sh.Data, Len: sh.Len, Cap: sh.Len, } return *(*[]byte)(unsafe.Pointer(&bh)) }
Genel olarak, özel durumlar dışında bu teknikleri kullanmamak daha iyidir — bu, veri güvenliğini ihlal eder (bir dizi aracılığıyla string'in içeriği değiştirilebilir, bu da hatalara yol açar).
“string`ten []byte'a dönüşümde, eğer string küçük ya da sabit ise ayrım yapmamak doğru mu?”
Doğru cevap: Hayır. String'in boyutlarından bağımsız olarak derleyici her zaman yeni bir byte dilimi oluşturur ve içeriği kopyalar. İstisnalar sadece güvenli olmayan optimizasyonlar içindir, bunlar safety garantisi vermez ve Go resmi dokümantasyonu tarafından desteklenmez.
Hikaye
Ekip yüksek yüklü bir log parse servis yazıyordu ve sürekli gelen stringleri byte dilimlerine ve geri çeviriyordu. Pik anlarda garbage collector, kısa ömürlü kopyaların toplanması için %30'a kadar CPU süresi tüketiyordu. Profil oluşturduktan sonra her dönüşüm string↔[]byte'nin ayrı bir bellek alanı tahsis ettiğini anladık. Havuzların ve API'nin yeniden tasarımının uygulanmasından sonra, dönüşümlerin önemli bir kısmını kaldırmayı başardık ve GC üzerindeki yükü iki kat azalttık.
Hikaye
Bir geliştirici JSON üzerindeki çalışmayı optimize etmeye çalışırken, allocations'ı önlemek için unsafe dönüşüm bytes→string kullandı. Başlangıçta bir performans artışı gözlemlendi, ancak bir ay sonra çökmeler başladı: bazı byte tamponları yeniden kullanıldı, string eski değiştirilen verilere işaret ediyordu. Düzeltmek ancak standart kopyalara dönmek ve işlem arası API'yi yeniden yapılandırmakla mümkün oldu.
Hikaye
Büyük ikili verileri ağ üzerinden aktarırken, serializasyonu "optimize etmeye" karar verdik, BytesToString (kopyalamadan) kullanarak. Bir gün gönderilen string, halka açık olarak görüldü ve byte diliminin içeriği hemen yeniden yazıldı, bu da çöplük ile özel veri parçalarının hata paketleri günlüklerine sızmasına neden oldu. Sonuç olarak, bellek deduplikasyonu, özel verilerin sızmasına yol açtı!