Operator new terlihat ringan di kode, tetapi setiap pemanggilannya menyisakan kewajiban: satu objek baru di managed heap yang kelak harus diklaim oleh garbage collector. Alokasi di .NET memang dirancang cepat, tetapi jumlah alokasi tetap menentukan seberapa sering GC perlu bekerja. Kita perlu tahu apa yang terjadi di balik new dan cara mengukurnya.
Mekanisme Alokasi di Managed Heap #
Runtime memelihara penunjuk khusus yang menunjuk ujung memori heap yang belum dipakai. Mengalokasikan objek berarti menggeser penunjuk sebesar ukuran objek, menuliskan header objek, dan mengembalikan alamatnya. Proses ini nyaris secepat penambahan variabel di stack. Biaya sesungguhnya muncul kemudian: semakin banyak objek yang dialokasikan, semakin cepat ruang generasi 0 penuh, dan semakin sering GC berjalan.
Setiap objek juga membawa overhead tetap untuk header dan penunjuk tipe. overhead itu sekitar 24 byte pada runtime 64-bit, sehingga objek data 10.000 karakter tidak menempati tepat 10.000 byte.
Contoh: Mengukur Alokasi Satu String #
long sebelum = GC.GetAllocatedBytesForCurrentThread();
var pesan = new string('x', 10_000);
long sesudah = GC.GetAllocatedBytesForCurrentThread();
Console.WriteLine($"Ukuran data: {pesan.Length} karakter");
Console.WriteLine($"Byte yang dialokasikan: {sesudah - sebelum}");Output yang Dihasilkan:
Ukuran data: 10000 karakter
Byte yang dialokasikan: 20024Mari kita bedah baris demi baris:
GC.GetAllocatedBytesForCurrentThread(): API ini melaporkan total byte yang dialokasikan thread saat ini sejak proses dimulai. Kita membaca nilainya sebelum dan sesudah pekerjaan, lalu mencari selisihnya.new string('x', 10_000): runtime mengalokasikan string berisi 10.000 karakter. Setiap karaktercharmenempati 2 byte, sehingga isi datanya 20.000 byte.- Selisih
sesudah - sebelum: hasilnya 20.024 byte. Angka 24 byte tambahan adalah overhead header objek, bukan bagian data. Selisih semacam ini disebut kontribusi alokasi.
Alokasi yang Sering Tak Disadari #
Beberapa pola kode mengalokasikan tanpa terlihat: penyambungan string di dalam loop membuat string baru pada setiap iterasi, query LINQ membuat objek enumerator dan delegate, dan boxing mengemas value type ke heap. Bab Allocation Reduction di akhir section ini membahas cara menekan pola-pola tersebut setelah kita memahami siklus referensi pada bab Reference Lifetime.