Kita mengukur alokasi pada bab Object Allocation dan melihat tekanannya pada bab Memory Pressure; kini saatnya menekannya. Pengurangan alokasi (allocation reduction) memberi dua keuntungan sekaligus: GC generasi 0 jarang penuh sehingga jeda GC berkurang, dan cache prosesor bekerja lebih baik karena data tidak terus-menerus ditulis ke lokasi baru. Teknik yang paling sering dipakai adalah mengganti penyambungan string dan memakai span untuk membaca data.
Contoh: Penyambungan String versus StringBuilder #
long sebelum = GC.GetAllocatedBytesForCurrentThread();
string teks = "";
for (int i = 0; i < 200; i++)
teks += "ab";
long sesudah = GC.GetAllocatedBytesForCurrentThread();
Console.WriteLine($"Penyambungan string : {sesudah - sebelum} byte");
sebelum = GC.GetAllocatedBytesForCurrentThread();
var sb = new System.Text.StringBuilder();
for (int i = 0; i < 200; i++)
sb.Append("ab");
sesudah = GC.GetAllocatedBytesForCurrentThread();
Console.WriteLine($"StringBuilder : {sesudah - sebelum} byte");
bool cocok = System.MemoryExtensions.StartsWith(teks.AsSpan(), "ababababab".AsSpan());
Console.WriteLine($"Pemeriksaan span : {cocok}");
Console.WriteLine($"Panjang hasil akhir : {teks.Length} karakter");Output yang Dihasilkan:
Penyambungan string : 85568 byte
StringBuilder : 1456 byte
Pemeriksaan span : True
Panjang hasil akhir : 400 karakterMari kita bedah baris demi baris:
teks += "ab"di dalam loop: string di .NET tidak dapat diubah (immutable), sehingga setiap iterasi membuat string baru yang menyalin seluruh isi string sebelumnya. Totalnya 85.568 byte untuk hasil akhir hanya 400 karakter.sb.Append("ab"):StringBuildermemelihara buffer internal yang diperbesar bertahap. Selisihnya hanya 1.456 byte, sekitar 60 kali lebih hemat.teks.AsSpan()danMemoryExtensions.StartsWith: span memberi jendela baca ke string tanpa menyalin apa pun, sehingga pemeriksaan awalan tidak mengalokasikan objek baru.teks.Length: hasil akhir kedua pendekatan identik; perbedaannya murni pada jumlah byte yang dialokasikan.
Daftar Teknik yang Lazim #
- Ganti penyambungan string berulang dengan
StringBuilderatau interpolasi satu kali. - Pakai
ReadOnlySpan<char>danMemoryExtensionsuntuk memotong, mencari, dan membandingkan teks. - Sewa buffer besar dari
ArrayPool<T>dan kembalikan setelah dipakai. - Simpan hasil perhitungan (caching) alih-alih menghitung ulang objek yang sama.
- Hindari boxing dengan API generik atau tipe eksplisit.
Semua teknik di atas berangkat dari prinsip yang sama: objek yang tidak pernah dibuat tidak perlu diklaim. Prioritaskan loop panas dan jalur pemrosesan berulang, karena alokasi di sana berlipat ribuan kali per detik dan menjadi penyumbang terbesar tekanan GC.