In Go führt die Umwandlung zwischen string und []byte normalerweise zu einem neuen Speicherzuweisung (Allocation), da Strings unveränderlich (immutable) sind, während Byte-Slices veränderlich (mutable) sind. Ausnahmen sind einige interne Optimierungen des Compilers (Escape-Analyse), aber diese sind nicht garantiert und ändern sich zwischen den Go-Versionen.
Direkte Umwandlung:
[]byte(s string): allokiert immer ein neues Byte-Array und kopiert die Daten.string(b []byte): kopiert ebenfalls immer die Daten aus dem Byte-Array in einen neuen String.Zero Allocation — Ansatz, bei dem wir überflüssige Zuweisungen vermeiden. In der Standardbibliothek von Go gibt es eine unsichere Umwandlung über das Paket unsafe, die es erlaubt, auf die gleichen Daten zuzugreifen, ohne sie zu kopieren. Dies sollte nur mit vollem Verständnis der Risiken verwendet werden.
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)) }
Überall, außer in besonderen Fällen, ist es besser, diese Techniken nicht zu verwenden — sie gefährden die Datensicherheit (man kann den Inhalt des Strings durch das gemeinsame Slice ändern, was zu Fehlern führt).
Oft wird gefragt: "Ist es wahr, dass bei der Umwandlung von string in []byte keine Zuweisung erfolgt, wenn der String klein oder konstant ist?"
Richtige Antwort: Nein. Unabhängig von der Größe des Strings wird der Compiler immer ein neues Byte-Slice erstellen und den Inhalt kopieren. Ausnahmen sind nur in unsicheren Optimierungen, die keine Sicherheit garantieren und nicht in der offiziellen Go-Dokumentation unterstützt werden.
Geschichte
Das Team entwickelte einen hochbelasteten Dienst zur Protokollanalyse und wandelte ständig eingehende Strings in Byte-Slices und zurück zur Verarbeitung um. In Spitzenzeiten benötigte der Garbage Collector bis zu 30% der CPU-Zeit für die Sammlung kurzlebiger Kopien. Nach der Profilerstellung wurde festgestellt: Jede Umwandlung von string↔[]byte allokierte einen separaten Speicherbereich. Nach der Einführung von Pools und der Neugestaltung der API konnten viele Konvertierungen entfernt werden, wodurch die Belastung des GC halbiert wurde.
Geschichte
Einer der Entwickler optimierte die Arbeit mit JSON, indem er die unsichere Umwandlung von Bytes→String zur Vermeidung von Zuweisungen verwendete. Zunächst war der Leistungszuwachs bemerkbar, aber nach einem Monat traten Abstürze auf: Ein bestimmter Byte-Puffer wurde wiederverwendet, der String zeigte auf alte, geänderte Daten. Dies konnte nur durch die Rückkehr zu den Standardkopien und die Neugestaltung der interprozessualen API behoben werden.
Geschichte
Bei der Übertragung großer binärer Daten über das Netzwerk beschlossen wir, die Serialisierung zu "optimieren", indem wir BytesToString verwendeten (ohne Kopierung). Eines Tages stellte sich heraus, dass der gesendete String öffentlich sichtbar war und der Inhalt des Byte-Slices sofort überschrieben wurde, was zu einer Übertragung von Müll und einem Leck privater Daten in die Protokolle fehlerhafter Pakete führte. Letztendlich führte die Speicher-Deduplication zu einem Leck privater Daten!