Deteksi Memory Leak — Debugging

Deteksi Memory Leak Memory leak terjadi ketika aplikasi mengalokasikan memori tetapi tidak pernah melepaskannya kembali. Seiring waktu, pemakaian memori terus b

Deteksi Memory Leak

Memory leak terjadi ketika aplikasi mengalokasikan memori tetapi tidak pernah melepaskannya kembali. Seiring waktu, pemakaian memori terus bertambah hingga akhirnya aplikasi menjadi lambat atau crash.

Penyebab Umum Memory Leak

1. Event Listener yang Tidak Di-cleanup

// ❌ Leak: listener terus menumpuk setiap mount
useEffect(() => {
  window.addEventListener("resize", handleResize);
  // Lupa cleanup!
}, []);

// ✅ Selalu cleanup saat unmount
useEffect(() => {
  window.addEventListener("resize", handleResize);
  return () => window.removeEventListener("resize", handleResize);
}, []);

2. setInterval / setTimeout yang Tidak Di-clear

// ❌ Interval berjalan terus meski komponen sudah unmount
useEffect(() => {
  const id = setInterval(fetchData, 5000);
  // Lupa clearInterval!
}, []);

// ✅ Clear saat cleanup
useEffect(() => {
  const id = setInterval(fetchData, 5000);
  return () => clearInterval(id);
}, []);

3. Reference ke DOM atau Object yang Sudah Tidak Dipakai

// ❌ Map terus tumbuh, tidak ada yang dihapus
const cache = new Map();

function cacheData(key, data) {
  cache.set(key, data); // Entry tidak pernah dihapus
}

// ✅ Gunakan WeakMap untuk referensi lemah
const cache = new WeakMap();
// Atau hapus entry yang sudah tidak diperlukan
cache.delete(key);

4. Closure yang Menahan Referensi

// ❌ Closure menahan referensi ke array besar
function createHandler() {
  const largeData = new Array(1000000).fill("data");
  return function handler() {
    // handler ini menahan largeData di memory!
    console.log("handled");
  };
}

Cara Mendeteksi Memory Leak

Chrome DevTools — Memory Tab

  1. Buka DevTools → tab Memory
  2. Pilih Heap snapshot → klik Take snapshot
  3. Lakukan aksi yang dicurigai menyebabkan leak
  4. Ambil snapshot lagi
  5. Bandingkan kedua snapshot — cari object yang terus bertambah

Allocation Timeline

  1. Pilih Allocation instrumentation on timeline
  2. Klik Start
  3. Lakukan beberapa kali aksi yang dicurigai
  4. Klik Stop
  5. Cari "retained" objects (tidak di-garbage collect)

Task Manager Browser

Shift+Esc → buka Task Manager Chrome. Amati kolom Memory footprint halaman kamu — jika terus naik saat digunakan, ada indikasi leak.

Mengukur Memory di JavaScript

// API Memory (hanya di Chrome)
const memory = performance.memory;
console.log({
  used: Math.round(memory.usedJSHeapSize / 1024 / 1024) + " MB",
  total: Math.round(memory.totalJSHeapSize / 1024 / 1024) + " MB",
  limit: Math.round(memory.jsHeapSizeLimit / 1024 / 1024) + " MB",
});

// Snapshot sederhana untuk monitoring
function takeMemorySnapshot(label) {
  if (performance.memory) {
    console.log(`[${label}] Heap: ${Math.round(performance.memory.usedJSHeapSize / 1024 / 1024)}MB`);
  }
}

Pola Deteksi Memory Leak

// Deteksi tren naik pada snapshot heap
function detectLeak(snapshots) {
  if (snapshots.length < 3) return false;

  // Hitung apakah memory konsisten naik
  let increasingCount = 0;
  for (let i = 1; i < snapshots.length; i++) {
    if (snapshots[i].heapUsed > snapshots[i - 1].heapUsed) {
      increasingCount++;
    }
  }

  // Jika >80% snapshot menunjukkan kenaikan → kemungkinan leak
  return increasingCount / (snapshots.length - 1) > 0.8;
}

Tips Mencegah Memory Leak

  1. Selalu cleanup useEffect — kembalikan fungsi cleanup
  2. Gunakan AbortController untuk membatalkan fetch saat unmount
  3. WeakRef dan WeakMap untuk cache yang tidak menghambat GC
  4. Hindari global variables yang menyimpan data besar
  5. Profile secara berkala — jangan tunggu sampai ada laporan lambat

Contoh: AbortController untuk Fetch

useEffect(() => {
  const controller = new AbortController();

  fetch("/api/data", { signal: controller.signal })
    .then(res => res.json())
    .then(setData)
    .catch(err => {
      if (err.name !== "AbortError") console.error(err);
    });

  return () => controller.abort(); // ✅ Batalkan fetch saat unmount
}, []);