package main

import (
	"fmt"
	"math"
	"runtime"
	"runtime/debug"
)

// sink membuat alokasi pasti terjadi: hasil make disimpan ke variabel
// global sehingga compiler tidak dapat menghapusnya.
var sink []byte

// alokasiSampah mengalokasikan totalMB memori berulang kali; potongan
// lama tertimpa dan menjadi sampah yang harus dibersihkan GC.
func alokasiSampah(totalMB int) {
	chunk := 256 << 10 // 256 KiB
	for diberikan := 0; diberikan < totalMB<<20; diberikan += chunk {
		sink = make([]byte, chunk)
	}
}

// jumlahSiklusGC membaca NumGC: jumlah siklus GC yang selesai sejak
// program start, penghitung kumulatif yang naik monoton.
func jumlahSiklusGC() uint32 {
	var m runtime.MemStats
	runtime.ReadMemStats(&m)
	return m.NumGC
}

func main() {
	// Bagian 1: matikan GC otomatis, buktikan siklus tidak bertambah.
	gogcAwal := debug.SetGCPercent(-1)
	fmt.Println("GOGC awal yang dikembalikan SetGCPercent:", gogcAwal)

	terakhir := jumlahSiklusGC()
	alokasiSampah(64)
	fmt.Println("Alokasi 64 MB sampah dengan GC mati : siklus bertambah", jumlahSiklusGC()-terakhir)

	// Bagian 2: runtime.GC() memaksa siklus berjalan meski GC otomatis mati.
	terakhir = jumlahSiklusGC()
	runtime.GC()
	runtime.GC()
	runtime.GC()
	fmt.Println("Tiga panggilan runtime.GC() paksa   : siklus bertambah", jumlahSiklusGC()-terakhir)

	// Bagian 3: aktifkan GC kembali, lalu pasang limit memori lunak.
	debug.SetGCPercent(gogcAwal)
	limitLama := debug.SetMemoryLimit(8 << 20)
	fmt.Println("Limit memori awal tidak terbatas    :", limitLama == math.MaxInt64)

	terakhir = jumlahSiklusGC()
	alokasiSampah(32)
	berjalan := jumlahSiklusGC() > terakhir
	fmt.Println("Alokasi 32 MB di bawah limit 8 MB   : GC tetap berjalan", berjalan)

	debug.SetMemoryLimit(limitLama)
	fmt.Println("Program selesai; limit lunak tidak mematikan proses")
}
