Memory leak terjadi ketika program mengalokasikan memori heap tetapi lupa memanggil free. Memori yang bocor tidak bisa dipakai ulang oleh program—bahkan setelah tidak ada pointer yang menunjuk ke blok tersebut. Pada program pendek, leak kecil tidak terasa; tetapi pada daemon atau server yang berjalan lama (jam, hari, minggu), leak terakumulasi sampai proses mengonsumsi seluruh RAM dan dibunuh oleh OOM-killer. Untungnya, AddressSanitizer (ASan) dan LeakSanitizer (LSan) yang terintegrasi di GCC dapat mendeteksi leak secara otomatis saat testing. Pada chapter ini kita akan membuat program yang sengaja bocor, lalu memverifikasi bahwa ASan menemukannya.
Apa itu Memory Leak #
Memory leak secara teknis adalah: blok heap yang masih dialokasikan (belum di-free) tetapi tidak ada pointer yang menunjuk ke blok tersebut. Akibatnya, allocator tidak tahu bahwa blok itu sebenarnya tidak terpakai—allocator tidak melacak pointer, hanya metadata blok. Program kehilangan akses ke blok tetapi allocator masih menganggap blok itu “dipakai”.
Penyebab umum memory leak:
- Lupa
free— alokasi tanpa dealokasi (kasus paling sederhana). - Pointer ditimpa —
p = malloc(...); ... p = malloc(...);tanpafree(p)di antaranya. - Branch early-return — fungsi keluar lebih awal (misalnya
returnsaat error) tanpafreedi cleanup path. - Ownership ambigu — dua fungsi mengira fungsi lain yang akan memanggil
free(topik chapter Ownership dan Lifetime). - Struct kompleks — linked list, tree, atau graph yang dibebaskan sebagian (inner node tidak ikut di-
free).
Mekanisme kerja di balik layar: setelah free, allocator glibc menandai blok sebagai bebas di free list dan dapat memakai ulang blok itu untuk malloc berikutnya. Tanpa free, blok tetap “terpakai” dari sudut pandang allocator sepanjang program berjalan. Pada akhir program, OS benar-benar membebaskan seluruh memori proses—jadi leak hanya bermasalah selama program berjalan, bukan setelah program keluar.
Demo: Leak Tanpa Sadar #
Kita tulis fungsi bocor yang mengalokasikan blok 10 elemen tetapi tidak pernah memanggil free. Fungsi ini dipanggil tiga kali—setiap panggilan meninggalkan satu blok 40 byte yang bocor.
#include <stdio.h>
#include <stdlib.h>
void bocor(size_t n) {
/* Malloc tanpa free di akhir fungsi -> memory leak */
int *data = malloc(n * sizeof(int));
if (data == NULL) return;
for (size_t i = 0; i < n; i++) data[i] = (int)i;
printf("bocor: data[0..%zu] = %d..%d (alamat %p)\n",
n - 1, data[0], data[n - 1], (void*)data);
/* TIDAK ada free(data) -> leak! */
}
int main(void) {
/* Memanggil bocor tiga kali -> tiga blok bocor */
for (int i = 0; i < 3; i++) {
bocor(10);
}
printf("Selesai, tetapi heap membawa blok yang tidak dibebaskan\n");
return 0;
}Output yang Dihasilkan:
bocor: data[0..9] = 0..9 (alamat 0x55932f65d2a0)
bocor: data[0..9] = 0..9 (alamat 0x55932f65e2e0)
bocor: data[0..9] = 0..9 (alamat 0x55932f65e310)
Selesai, tetapi heap membawa blok yang tidak dibebaskanProgram berjalan “normal” dan tidak ada error yang dilaporkan. Inilah sifat memory leak yang berbahaya: bug tidak terdeteksi tanpa alat bantu khusus. Tanpa ASan, kita harus memakai Valgrind (valgrind --leak-check=full ./program) yang memeriksa seluruh alokasi heap saat program keluar.
Deteksi dengan AddressSanitizer #
AddressSanitizer (ASan) dengan LeakSanitizer (LSan) terintegrasi di GCC dapat mendeteksi leak secara otomatis. Kita kompilasi program yang sama dengan flag -fsanitize=address,undefined:
gcc -std=c11 -Wall -Wextra -fsanitize=address,undefined \
-o /tmp/test_leak_asan /home/z/my-project/work/tests/test_leak.c
/tmp/test_leak_asanOutput yang Dihasilkan:
bocor: data[0..9] = 0..9 (alamat 0x602000000010)
bocor: data[0..9] = 0..9 (alamat 0x602000000030)
bocor: data[0..9] = 0..9 (alamat 0x602000000050)
Selesai, tetapi heap membawa blok yang tidak dibebaskan
=================================================================
==24341==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 120 byte(s) in 3 object(s) allocated from:
#0 0x7f057b3c5c57 in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69
#1 0x557aeee05207 in bocor (/tmp/test_leak_asan+0x1207) (BuildId: 20ea3044e33bd7b5bc83d922f930b05282ec5f60)
#2 0x557aeee0543c in main (/tmp/test_leak_asan+0x143c) (BuildId: 5e5b51f4c39af9ac589408c1dc432ba390a273cc)
#3 0x7f057aaf3ca7 (/lib/x86_64-linux-gnu/libc.so.6+0x29ca7) (BuildId: 5e5b51f4c39af9ac589408c1dc432ba390a273cc)
SUMMARY: AddressSanitizer: 120 byte(s) leaked in 3 allocation(s).ASan mengonfirmasi adanya 3 blok bocor dengan total 120 byte (40 byte per blok = 10 elemen int). Stack trace menunjukkan bahwa blok-blok tersebut dialokasikan dari fungsi bocor yang dipanggil dari main. Informasi ini sangat berharga—kita tahu persis baris mana yang menyebabkan leak.
Mari kita telaah baris-baris kode di atas:
- Baris 5 (
void bocor(size_t n)): fungsi yang seharusnya mengembalikan buffer bertugas menyelesaikan suatu operasi. Sayangnya,bocorlupa memanggilfreesebelum kembali. - Baris 7 (
int *data = malloc(n * sizeof(int));): alokasin * sizeof(int)= 40 byte. Pointerdatahanya ada dalam stack framebocor—saat fungsi kembali,datahilang tetapi blok heap tetap dialokasikan. - Baris 9-10 (
for ... data[i] = ...): mengisi blok. Data ini akan terkubur di heap tanpa pernah bisa diakses lagi. - Baris 15 (
}akhir fungsibocor): fungsi kembali tanpafree(data). Inilah titik leak terjadi. - Baris 21 (
for (int i = 0; i < 3; i++)): loop tiga kali, tiga kali leak. Setiap iterasi menambah 40 byte ke total leak. - Baris 25 (
return 0;): program keluar. Pada program normal, OS membebaskan seluruh memori proses—tapi selama program berjalan, 120 byte telah “hilang”.
Memperbaiki Leak #
Perbaikan sangat sederhana: panggil free sebelum fungsi kembali.
void tidak_bocor(size_t n) {
int *data = malloc(n * sizeof(int));
if (data == NULL) return;
for (size_t i = 0; i < n; i++) data[i] = (int)i;
printf("tidak_bocor: alamat %p\n", (void*)data);
free(data); /* PERBAIKAN: bebaskan blok sebelum return */
}Setelah perbaikan ini, kompilasi ulang dengan ASan dan jalankan kembali—LSan akan melaporkan “0 byte(s) leaked”. Inilah indikator bahwa program bersih dari leak.