golangGreen Tea GC: Garbage Collector Generasi Baru di Go

Tags: Go Runtime Β· GC Internals Β· Go 1.25 Β· Performance Β· GOEXPERIMENT=greenteagc


Daftar Isi

  1. Pengantar: Masalah yang Mengintai di Balik GC Go

  2. Rekap Cepat: Cara Kerja GC Go Saat Ini

  3. Masalah Nyata: 35% CPU Terbuang Sia-Sia

  4. Tantangan Hardware Modern

  5. Green Tea: Prinsip Inti & Cara Kerjanya

  6. Dua Bit, Satu Halaman β€” Metadata yang Cerdas

  7. Akselerasi Vektor dengan AVX-512

  8. Hasil & Performa Nyata

  9. Kapan Green Tea Membantu (dan Kapan Tidak)

  10. Cara Menggunakan Green Tea Sekarang

  11. Masa Depan & Perjalanan yang Telah Ditempuh


1. Pengantar: Masalah yang Mengintai di Balik GC Go {#1-pengantar}

Go sudah lama dikenal memiliki garbage collector yang sangat baik. Sejak Go 1.5, jeda stop-the-world berhasil dipangkas dari ratusan milidetik menjadi di bawah satu milidetik. Bagi banyak pengembang, ini sudah lebih dari cukup.

Namun ada satu masalah yang jauh lebih dalam yang hampir tidak pernah terlihat di permukaan: bukan soal seberapa lama program berhenti, tapi soal seberapa banyak CPU yang dihabiskan GC saat ia berjalan secara konkuren dengan program kita. Tidak jarang sebuah aplikasi Go menghabiskan 10–20% dari total CPU-nya hanya untuk garbage collection. Di beberapa kasus, angka itu bisa mencapai 40% lebih.

πŸ’‘ Konteks Penting: Studi internal tim Go menunjukkan bahwa lebih dari 35% siklus CPU di dalam loop inti GC terbuang hanya untuk menunggu data dari memori utama β€” belum termasuk efek domino yang ditimbulkannya. Ini bukan masalah kecil; ini adalah inefisiensi struktural dari algoritma yang mendasarinya.

Di sinilah Green Tea hadir. Diperkenalkan sebagai eksperimen di Go 1.25 dan direncanakan menjadi default di Go 1.26, Green Tea bukan sekadar optimasi tambalan β€” melainkan perancangan ulang mendasar dari cara GC menelusuri heap, berangkat dari satu ide yang mengejutkan sederhana namun butuh bertahun-tahun untuk ditemukan dan dibuktikan.


2. Rekap Cepat: Cara Kerja GC Go Saat Ini {#2-rekap-gc}

Untuk memahami apa yang baru dari Green Tea, kita perlu memahami dulu apa yang sudah ada. GC Go saat ini mengimplementasikan algoritma Tri-Color Mark-and-Sweep yang konkuren.

Bayangkan semua objek di heap sebagai simpul dalam sebuah graf, dan semua pointer sebagai sisi penghubungnya. GC pada dasarnya melakukan graph flood β€” penelusuran graf β€” dari titik-titik akar (variabel global dan variabel di stack goroutine) untuk menemukan semua objek yang masih dapat dijangkau oleh program.

Tiga Warna, Tiga Status

Setiap objek di heap memiliki salah satu dari tiga "warna" yang mewakili statusnya dalam siklus GC:

Putih (White) berarti objek belum ditemukan sama sekali. Di akhir siklus, objek yang tetap putih adalah sampah yang akan dihapus memorinya.

Abu-abu (Gray) berarti objek sudah ditemukan sebagai terjangkau, tapi isi referensinya belum selesai ditelusuri. Objek abu-abu ada dalam antrian kerja.

Hitam (Black) berarti objek sudah selesai diperiksa sepenuhnya β€” ia terjangkau dan semua pointer di dalamnya sudah diproses.

Empat Fase Siklus GC

Fase 1 β€” Mark Setup (Stop-the-World singkat). GC mengaktifkan write barrier dan menandai semua root objects sebagai abu-abu. Jeda ini biasanya hanya puluhan hingga ratusan mikrodetik.

Fase 2 β€” Concurrent Marking. Fase terlama, dan ia berjalan bersamaan dengan program. GC mengambil objek abu-abu satu per satu, menelusuri semua referensinya, menandainya abu-abu, lalu menandai objek yang selesai diperiksa menjadi hitam.

Fase 3 β€” Mark Termination (Stop-the-World singkat kedua). Memastikan tidak ada objek abu-abu yang tersisa, lalu menonaktifkan write barrier.

Fase 4 β€” Concurrent Sweeping. Memori objek putih (tak terjangkau) dibebaskan secara inkremental dan lazy β€” dilakukan sedikit demi sedikit saat ada alokasi baru, bukan sekaligus.

Algoritma ini brilian karena berjalan konkuren. Masalahnya bukan pada desainnya β€” tapi pada bagaimana ia berinteraksi dengan hardware modern.


3. Masalah Nyata: 35% CPU Terbuang Sia-Sia {#3-masalah-cpu}

Di permukaan, GC Go terlihat baik-baik saja. Namun jika kita intip profil CPU dari GC yang sedang berjalan, kita menemukan fakta yang tidak nyaman: sekitar 85% waktu GC dihabiskan di loop inti pemindaian (scan loop), dan dari waktu itu, lebih dari 35% hanya diisi dengan CPU yang sedang menunggu data datang dari memori utama.

Ini bukan karena kode GC-nya lambat. Ini adalah konsekuensi struktural dari cara algoritma graph flood bekerja.

Analogi Jalan Raya vs Jalan Kota

Bayangkan CPU seperti mobil yang sedang berkendara. Untuk mencapai kecepatan tinggi, CPU butuh bisa melihat jauh ke depan dan jalan yang lurus β€” inilah yang terjadi saat memproses data yang tersusun rapi dan berurutan di memori.

Tapi algoritma graph flood memaksa CPU untuk berkendara di jalan kota yang penuh persimpangan. Setiap kali GC memindai sebuah objek, ia harus mengikuti pointer ke objek berikutnya yang bisa berada di mana saja di heap β€” beda halaman, beda bagian memori, jauh dari yang sebelumnya. CPU tidak bisa memprediksi ke mana harus pergi dan terus-menerus harus berhenti menunggu di "lampu merah" memori.

⚠️ Masalah Cache Miss: CPU modern sangat bergantung pada hierarki cache (L1, L2, L3). Mengakses L1 cache hanya butuh ~1 siklus CPU, tapi mengakses RAM (DRAM) bisa butuh 200–400 siklus. Graph flood hampir tidak pernah menggunakan cache secara efektif karena ia melompat ke area memori yang berbeda-beda setiap saat β€” memaksa CPU untuk terus meminta data dari RAM dan menunggu.


4. Tantangan Hardware Modern yang Memperburuk Keadaan {#4-hardware}

Yang lebih mengkhawatirkan, masalah ini tidak akan membaik dengan sendirinya seiring hardware semakin canggih. Justru sebaliknya β€” tren hardware modern semakin memperburuk kondisi GC Go.

NUMA (Non-Uniform Memory Access). Pada CPU modern multi-chip, memori diasosiasikan dengan subset inti prosesor tertentu. Core yang mengakses memori "milik" core lain akan mendapatkan latensi yang jauh lebih tinggi. Graph flood yang melompat-lompat secara acak akan sering terjebak dalam situasi ini.

Bandwidth memori per CPU yang menurun. Meski jumlah core CPU terus bertambah, bandwidth memori yang tersedia per core justru trennya menurun. Artinya setiap core harus mengantre lebih lama untuk akses memori.

Jumlah core yang terus bertambah. Algoritma graph flood paralel menggunakan antrian objek bersama. Semakin banyak core yang bekerja secara paralel, semakin besar potensi bottleneck dan perebutan akses pada antrian tersebut.

Fitur vektor yang tak bisa dimanfaatkan. CPU modern punya instruksi vektor seperti AVX-512 yang bisa memproses banyak data sekaligus. Tapi graph flood sama sekali tidak bisa memanfaatkannya karena pekerjaannya terlalu tidak teratur β€” ukuran objek bervariasi, lokasi acak, dan setiap langkah bergantung pada hasil langkah sebelumnya.

πŸ”­ Tim Go mencatat ada ironi yang menyedihkan: untuk kebanyakan kode, menunggu dua tahun artinya kode kita jalan lebih cepat karena hardware makin bagus. Tapi untuk Go sebagai bahasa dengan GC berbasis mark-sweep, tren hardware justru berisiko membuat kode lebih lambat dari waktu ke waktu tanpa perubahan algoritmik.


5. Green Tea: Prinsip Inti & Cara Kerjanya {#5-green-tea}

Setelah bertahun-tahun eksplorasi β€” benih idenya bahkan dimulai sejak 2018 β€” tim Go akhirnya menemukan pendekatan yang berhasil. Inti dari Green Tea adalah satu kalimat yang mengejutkan sederhana:

🍡 "Bekerjalah dengan halaman (pages), bukan dengan objek individual."

Terdengar trivial, bukan? Namun di baliknya tersembunyi kompleksitas yang luar biasa β€” bagaimana mengurutkan penelusuran graf, metadata apa yang perlu dilacak, dan kapan pendekatan ini benar-benar lebih efisien daripada graph flood klasik.

Apa itu Halaman (Page)?

Dalam konteks Go runtime, sebuah halaman (span dalam terminologi kode) adalah blok memori berukuran tetap sebesar 8 KiB yang berisi objek-objek dengan ukuran yang sama. Ukuran 8 KiB ini tidak dipilih sembarangan β€” ini cukup besar untuk menampung beberapa hingga puluhan objek, namun cukup kecil untuk muat seluruhnya di dalam cache CPU.

Green Tea hanya fokus pada small object spans β€” halaman yang berisi objek berukuran hingga 512 byte. Ini mencakup sebagian besar objek yang umum dibuat di program Go: struct, node tree, buffer kecil, objek hasil parsing JSON, dan sebagainya.

Perbedaan Fundamental dari Graph Flood

Aspek
GC Lama (Object-Centric)
Green Tea (Page-Centric)

Work list berisi

Objek individual

Halaman utuh

Setiap item

Masuk work list sekali

Halaman bisa masuk berkali-kali

Urutan penelusuran

Depth-first (stack/LIFO)

Breadth-first (queue/FIFO)

Penyimpanan metadata

Tersebar secara global

Lokal per halaman

Pola akses memori

Melompat acak ke seluruh heap

Memindai objek yang berdekatan

Instruksi vektor

Tidak bisa digunakan

Bisa pakai AVX-512

Alur Kerja Green Tea, Langkah demi Langkah

Langkah 1 β€” Temukan pointer ke sebuah objek. Sama seperti GC lama, Green Tea mengikuti pointer dari root. Tapi ketika menemukan pointer ke objek di halaman X, ia tidak langsung menaruh objek itu ke work list. Sebaliknya, ia menaruh halaman X secara keseluruhan ke dalam antrian (FIFO queue).

Langkah 2 β€” Tandai objek sebagai "seen" di dalam metadata halaman. Objek yang ditemukan tidak dilacak secara global, tapi hanya melalui bit metadata lokal di dalam halaman miliknya. Ini jauh lebih hemat memori dan lebih cache-friendly.

Langkah 3 β€” Biarkan objek terakumulasi. Karena antrian halaman bersifat FIFO, sebuah halaman bisa menunggu di antrian cukup lama. Selama menunggu, bisa jadi ada lebih banyak pointer ditemukan yang menunjuk ke objek-objek lain dalam halaman yang sama. Ini disebut "akumulasi kemalasan" yang sengaja dirancang β€” laziness is a virtue dalam konteks ini.

Langkah 4 β€” Pindai satu halaman penuh secara berurutan. Saat sebuah halaman akhirnya diproses, GC memindai semua objek yang sudah ditandai "seen" dalam halaman itu secara berurutan dari atas ke bawah. Inilah kuncinya: pemindaian berurutan dalam blok memori yang sama, bukan melompat-lompat.

Langkah 5 β€” Halaman bisa kembali ke antrian. Berbeda dengan GC lama di mana sebuah objek hanya masuk work list sekali, sebuah halaman di Green Tea bisa kembali ke antrian jika ditemukan lebih banyak objek baru di dalamnya. Ini tidak masalah karena bit metadata memastikan kita tidak memindai objek yang sama dua kali.


6. Dua Bit, Satu Halaman β€” Metadata yang Cerdas {#6-metadata}

Salah satu perubahan paling elegan di Green Tea adalah pada sistem metadata-nya. GC lama hanya menyimpan satu bit per objek (apakah sudah dikunjungi atau belum). Green Tea menggunakan dua bit per objek:

Bit "Seen" menandai bahwa ada pointer yang menunjuk ke objek ini β€” artinya objek ini perlu dipindai suatu saat nanti. Bit ini di-set saat pointer ke objek pertama kali ditemukan.

Bit "Scanned" menandai bahwa objek ini sudah selesai dipindai β€” semua pointer di dalamnya sudah diproses. Bit ini di-set setelah pemindaian selesai.

Dengan dua bit ini, GC bisa dengan mudah menentukan "objek mana yang masih perlu dipindai dalam halaman ini?" hanya dengan melihat selisih antara kumpulan bit Seen dan bit Scanned. Tidak perlu mencari ke seluruh heap β€” semua informasi ada di dalam halaman itu sendiri, yang dengan sendirinya cenderung ada di cache CPU.

Ilustrasi status bit untuk sebuah halaman:

πŸ’‘ Mengapa Ini Lebih Efisien? Bayangkan seluruh metadata untuk sebuah halaman 8 KiB berisi maksimal 512 objek. Dua bit per objek berarti total metadata per halaman hanya 128 byte β€” cukup kecil untuk dimuat seluruhnya ke dalam satu atau dua cache line CPU. GC lama harus membaca metadata yang tersebar di seluruh heap; Green Tea membacanya dari satu lokasi yang selalu ada di cache.


7. Akselerasi Vektor dengan AVX-512 {#7-avx512}

Karena objek-objek dalam satu halaman punya ukuran yang seragam dan metadata yang terstruktur rapi, Green Tea membuka pintu untuk sesuatu yang sebelumnya mustahil: menggunakan instruksi vektor AVX-512 untuk memproses seluruh halaman sekaligus.

Sebagian besar CPU x86 modern mendukung AVX-512, yang memiliki register vektor selebar 512 bit. Metadata dua bit untuk seluruh halaman bisa muat hanya dalam dua register vektor β€” langsung di CPU, tanpa perlu membaca memori lagi. Dan dengan instruksi VGF2P8AFFINEQB (instruksi dari ekstensi Galois Field), Go bisa melakukan transformasi bitmap yang kompleks hanya dalam beberapa siklus CPU.

Kernel Scanning AVX-512 β€” Enam Langkah

1. Ambil bit "seen" dan "scanned" untuk seluruh halaman. Karena ukurannya kecil (128 byte), data ini sudah ada di cache atau bisa dimuat dalam satu instruksi.

2. Bandingkan kedua bitmap. Gabungan (union) menjadi bit "scanned" yang baru. Selisih (difference) menjadi active objects bitmap β€” daftar objek yang belum dipindai dalam pass ini.

3. Ekspansi bitmap objek ke bitmap kata (word). Karena setiap objek terdiri dari beberapa kata (8 byte per kata), setiap bit di active objects bitmap di-"lebarkan" menjadi N bit sesuai ukuran objek. Misalnya untuk objek 6-kata (48 byte), setiap bit diubah menjadi 6 bit:

Instruksi VGF2P8AFFINEQB melakukan operasi matriks bit ini dalam waktu konstan, dengan matriks berbeda untuk setiap ukuran objek.

4. Ambil bitmap pointer/scalar untuk halaman. Data ini tersimpan oleh memory allocator β€” setiap bit menandai apakah kata tertentu berisi pointer atau nilai skalar biasa.

5. Interseksi dua bitmap. Hasilnya adalah active pointer bitmap: lokasi tepat setiap pointer dalam objek yang perlu dipindai. Semua ini terjadi dalam satu operasi vektor.

6. Kumpulkan semua pointer dengan memproses 64 byte sekaligus menggunakan instruksi vektor, lalu masukkan hasilnya ke buffer pemrosesan.

βš™οΈ Catatan Teknis: Instruksi VGF2P8AFFINEQB melakukan transformasi afin pada field Galois GF(2), di mana perkalian adalah AND dan penjumlahan adalah XOR. Seluruh implementasinya tersedia di repositori Go open source β€” Anda bisa membaca kode assembly-nya langsung.


8. Hasil & Performa Nyata {#8-hasil}

Pertanyaan yang paling penting: seberapa besar peningkatannya?

Metrik
Nilai

Reduksi overhead GC (tanpa AVX-512)

10–40% tergantung workload

Reduksi overhead GC (dengan AVX-512)

Tambahan ~10% pada hardware yang mendukung

Reduksi cache miss L1/L2 pada GC-heavy benchmark

~35%

Status produksi

Sudah dipakai di Google secara internal

Angka-angka ini bukan hanya dari benchmark sintetis. Tim Google telah me-roll out Green Tea di lingkungan produksi internal mereka dan mendapatkan hasil yang serupa. Benchmark tile38 (database geospasial yang berat dengan struktur tree) menunjukkan pengurangan overhead GC hingga 35%. Workload yang mengalokasikan banyak objek kecil dalam batch β€” seperti parsing JSON, memproses hasil query database, membangun AST β€” juga mendapat manfaat signifikan.

Untuk melihat dampak nyata: jika sebuah aplikasi menghabiskan 10% CPU untuk GC dan Green Tea menguranginya 30%, maka total penggunaan CPU turun 3%. Bagi layanan yang melayani jutaan request per hari, ini setara dengan menghemat ratusan server.


9. Kapan Green Tea Membantu (dan Kapan Tidak) {#9-kapan}

Green Tea bukan peluru ajaib yang cocok untuk semua skenario. Memahami kapan ia efektif dan kapan tidak membantu kita membuat keputusan yang tepat.

Workload yang Paling Diuntungkan

Green Tea bekerja paling baik ketika heap program memiliki "lokalitas alami" β€” banyak objek yang saling berhubungan cenderung berada dalam halaman memori yang sama atau berdekatan.

Ini umumnya terjadi pada program dengan struktur tree atau graf dengan fan-out tinggi, di mana satu node memiliki banyak anak yang juga berukuran kecil. Juga pada batch processing β€” program yang mengalokasikan banyak objek kecil sekaligus, seperti parsing JSON besar, memproses baris-baris dari database, atau membangun AST dari kode sumber. Karena objek-objek ini dialokasikan berurutan, mereka cenderung berada dalam halaman yang sama.

Workload yang Kurang Diuntungkan

Di sisi lain, Green Tea tidak banyak membantu β€” bahkan bisa sedikit lebih lambat β€” untuk workload yang memiliki heap dengan fan-out rendah dan banyak mutasi. Bayangkan program yang terus-menerus memperbarui referensi antara objek di berbagai bagian heap yang berbeda; ini menyebabkan objek-objek yang saling terhubung tersebar acak, sehingga manfaat lokalitas halaman menjadi minimal.

⚠️ Pelajaran dari DoltHub: DoltHub, pembuat database relasional berbasis Git, menguji Green Tea di September 2025 dan menemukan hasil netral β€” setiap siklus GC menjadi lebih mahal (karena overhead akumulasi halaman), namun GC berjalan lebih jarang. Secara keseluruhan netral, bukan regresi. Tim Go telah memperbaiki pola ini sebelum Go 1.26 rilis, namun pelajarannya tetap berlaku: ukur dulu, jangan berasumsi.

Green Tea juga hanya berlaku untuk objek kecil (≀512 byte). Objek besar tetap ditangani oleh algoritma GC konvensional β€” perubahan ini transparan dan otomatis.


10. Cara Menggunakan Green Tea Sekarang {#10-cara-pakai}

Di Go 1.25 (Eksperimental)

Green Tea tersedia sebagai eksperimen di Go 1.25. Anda perlu mengaktifkannya secara eksplisit saat build:

Di Go 1.26 (Default)

Berdasarkan data yang telah dikumpulkan, tim Go berencana menjadikan Green Tea sebagai GC default di Go 1.26. Jika Anda mengalami masalah, Anda masih bisa menonaktifkannya:

Perlu dicatat bahwa opsi opt-out ini akan dihapus di Go 1.27, jadi jika Anda menemukan masalah, sangat disarankan untuk segera melaporkannya kepada tim Go.

Mengukur Dampak di Aplikasi Anda

Cara paling langsung untuk melihat apakah Green Tea membantu aplikasi Anda adalah membandingkan profil GC sebelum dan sesudah. Aktifkan gctrace untuk melihat statistik per siklus:

Output gctrace akan terlihat seperti ini:

Kolom penting yang perlu diperhatikan adalah angka persentase kedua (8% pada contoh di atas) yang menunjukkan persentase waktu yang dihabiskan dalam GC, dan kolom memori (28->28->14 MB) yang menunjukkan ukuran heap sebelum mark, setelah mark, dan setelah sweep. Jika persentase GC turun secara konsisten dengan Green Tea, itu pertanda baik.

Untuk analisis lebih mendalam, gunakan pprof dengan profil heap dan CPU:

Tips Penulisan Kode yang Bersahabat dengan Green Tea

Untuk memaksimalkan manfaat Green Tea, kode yang mengalokasikan banyak objek kecil dari jenis yang sama secara berurutan akan mendapat manfaat terbesar, karena objek-objek tersebut akan masuk ke halaman yang sama.


11. Masa Depan & Perjalanan yang Telah Ditempuh {#11-masa-depan}

Green Tea bukan akhir dari perjalanan β€” ia adalah awal dari babak baru. Di Go 1.26, bukan hanya Green Tea yang akan menjadi default, tapi juga akselerasi vektor AVX-512 akan diaktifkan pada hardware yang mendukungnya (Intel Ice Lake dan AMD Zen 4 ke atas), membawa tambahan pengurangan overhead GC sekitar 10%.

Yang menarik juga adalah perjalanan manusia di balik teknologi ini. Benih idenya ada sejak 2018, namun "semua orang di tim pikir orang lain yang menemukan ide pertama kali" β€” mencerminkan bagaimana inovasi sejati lahir dari kolaborasi banyak pikiran.

Nama "Green Tea" sendiri lahir di 2024 ketika Austin Clements mengerjakan prototipe sambil cafe crawling di Jepang dan minum banyak matcha. Prototipe itu membuktikan bahwa inti dari ide Green Tea memang layak untuk dikejar. Dari sana, Michael Knyszek mengerjakan implementasi dan produksinya sepanjang 2025, dengan ide yang terus berevolusi dan berubah.

πŸ“¬ Cara Berkontribusi: Tim Go aktif mencari feedback. Jika Green Tea membantu aplikasi Anda, balas di existing issue #73581arrow-up-right di GitHub Go. Jika ada masalah, buka new issue. Data dari komunitas sangat membantu untuk menyempurnakan Go 1.26 dan seterusnya.

Masa depan Green Tea juga menjanjikan. Riset lanjutan mencakup kemungkinan menggunakan concentrator network untuk menciptakan lokalitas buatan bahkan pada workload yang heap-nya tersebar, dan skalabilitas pada sistem dengan ratusan core CPU yang semakin massively parallel.


Ringkasan Eksekutif

Green Tea mengubah cara pandang GC Go dari "mengejar satu objek ke objek lain" menjadi "bekerja dalam blok memori yang terstruktur." Dengan melacak halaman memori 8 KiB alih-alih objek individual, Green Tea menciptakan pola akses memori yang jauh lebih ramah terhadap cache CPU modern, membuka pintu untuk akselerasi vektor, dan mengurangi overhead sinkronisasi pada sistem multicore.

Hasilnya adalah pengurangan overhead GC 10–40% untuk sebagian besar workload, sudah production-ready di Google, dan akan menjadi default di Go 1.26. Jika Anda menggunakan Go 1.25, Anda bisa mencobanya hari ini dengan satu variabel lingkungan: GOEXPERIMENT=greenteagc.


Referensi: go.dev/blog/greenteagcarrow-up-right Β· GitHub Issue #73581arrow-up-right Β· GopherCon 2025 (Michael Knyszek & Austin Clements) Β· siddharthav.medium.comarrow-up-right

Last updated