Memory Leaks di JavaScript: Cara Deteksi dan Pencegahannya
Daftar Isi
- Pendahuluan
- Apa Itu Memory Leak?
- Pola Memory Leak yang Paling Sering Terjadi
- Cara Mendeteksi Leak dengan DevTools
- Checklist Pencegahan
- Tips Monitoring di Production
- FAQ
Pendahuluan
JavaScript memang punya garbage collector, tapi itu bukan berarti memory leak tidak mungkin terjadi. Garbage collector hanya bisa membebaskan memori yang sudah tidak punya referensi. Kalau ada referensi yang tidak sengaja tetap hidup, penggunaan memori akan terus naik.
Di aplikasi seperti SPA, dashboard, atau admin panel yang dibuka lama, masalah ini biasanya muncul sebagai UI makin berat, animasi patah-patah, dan tab browser bisa crash di device dengan RAM terbatas.
Artikel ini fokus ke pola leak yang sering kejadian di project nyata, plus cara cepat menemukannya.
Apa Itu Memory Leak?
Memory leak adalah kondisi saat memori yang seharusnya bisa dilepas tetap tertahan karena masih ada referensi ke object tersebut.
Di JavaScript, penyebab umumnya:
- Node DOM yang sudah dihapus tapi masih direferensikan
- Closure yang menahan object besar lebih lama dari yang dibutuhkan
- Timer atau interval yang terus jalan setelah komponen unmount
- Cache global yang terus bertambah tanpa batas
- Event listener ditambah berulang tapi tidak pernah di-remove
Rule of thumb yang simpel: selama object masih bisa dijangkau dari root reference (global scope, stack aktif, timer, atau DOM reference), object itu tidak akan dibersihkan.
Pola Memory Leak yang Paling Sering Terjadi
1. Event Listener Tidak Dilepas
function mountModal() {
const modal = document.querySelector('#modal');
function onResize() {
modal.style.maxHeight = `${window.innerHeight - 40}px`;
}
window.addEventListener('resize', onResize);
return function unmount() {
window.removeEventListener('resize', onResize);
};
}
Kalau cleanup lupa dipanggil, setiap mount akan menambah listener baru dan menyimpan closure yang tidak perlu.
2. Timer yang Lupa Dihentikan
let intervalId;
export function startPolling(fetchStats) {
intervalId = setInterval(() => {
fetchStats();
}, 5000);
}
export function stopPolling() {
clearInterval(intervalId);
}
Interval adalah sumber leak klasik, terutama di komponen yang sering mount/unmount.
3. Referensi Detached DOM
const detached = [];
function removeItem(itemEl) {
detached.push(itemEl);
itemEl.remove();
}
Elemen sudah dihapus dari DOM, tapi tetap hidup di memori karena array detached masih menyimpan referensinya.
4. Cache Tanpa Batas
const responseCache = new Map();
export function remember(key, value) {
responseCache.set(key, value);
}
Tanpa strategi eviction, memori akan terus naik. Minimal pakai TTL, LRU, atau max size.
Cara Mendeteksi Leak dengan DevTools
Langkah 1: Lihat Tren Heap
Buka Chrome DevTools -> Performance Monitor, lalu pantau:
- JS heap size
- Jumlah DOM node
- Jumlah event listener
Kalau angka-angka ini terus naik setelah flow user diulang, besar kemungkinan ada leak.
Langkah 2: Ambil Heap Snapshot
- Buka tab Memory
- Ambil Snapshot A
- Jalankan flow beberapa kali (buka/tutup modal, pindah route, dsb.)
- Paksa garbage collection
- Ambil Snapshot B
- Bandingkan retained size
Cari object atau node yang harusnya sudah hilang tapi masih tertahan.
Langkah 3: Gunakan Allocation Timeline
Rekam alokasi memori sambil mengulang flow yang dicurigai. Kalau setelah GC baseline tidak turun lagi, biasanya ada referensi yang nyangkut.
Langkah 4: Cek Detached Nodes
Di hasil snapshot, filter dengan kata “Detached”. Ini salah satu indikator tercepat untuk menemukan leak di aplikasi frontend.
Checklist Pencegahan
- Selalu sediakan fungsi cleanup di hooks atau lifecycle
- Setiap listener yang ditambah harus punya remove
- Clear semua timer saat teardown
- Hindari global variable yang tidak perlu
- Gunakan
WeakMap/WeakSetkalau entry tidak boleh menahan object tetap hidup - Batasi ukuran cache dan masa aktif cache
- Unsubscribe stream (WebSocket, RxJS, EventSource)
- Review closure yang menangkap object besar
Prinsip praktis untuk tim: setiap side effect baru harus punya jalur cleanup yang jelas di file yang sama.
Tips Monitoring di Production
Debugging lokal saja tidak cukup. Tambahkan sinyal ringan di production:
- Pantau tren penggunaan memori di sesi panjang
- Alert saat crash tab meningkat di halaman tertentu
- Korelasikan perpindahan route dengan lonjakan heap
- Sampling jumlah listener pada internal QA build
Kamu juga bisa bikin synthetic test yang mengulang flow kritikal berkali-kali, lalu fail jika pertumbuhan heap melewati ambang batas.
FAQ
Apakah kenaikan memori selalu berarti leak?
Tidak selalu. Ada kenaikan yang normal karena cache, JIT optimization, atau proses warmup. Disebut leak kalau pertumbuhan bersifat persisten dan tidak turun lagi setelah flow selesai plus GC dijalankan.
Haruskah semua Map diganti ke WeakMap?
Tidak. WeakMap hanya cocok jika key-nya object dan kamu memang ingin entry hilang otomatis saat key tidak direferensikan lagi.
Apakah framework modern otomatis bebas memory leak?
Tidak juga. Framework membantu, tapi leak tetap bisa muncul dari side effect custom, library pihak ketiga, atau cleanup logic yang terlewat.
Di project kamu, leak paling sering datang dari mana: listener, timer, atau cache? Cerita pengalaman debugging-mu di kolom komentar.