Stack versus Heap

Perbedaan masa hidup memori stack dan heap serta alasan heap diperlukan.

Daftar isi

Setiap data yang disentuh program C berada pada salah satu dari dua wilayah memori, yaitu stack dan heap. Stack menyimpan variabel lokal dan data pemanggilan fungsi, sedangkan heap menyimpan blok memori yang dikelola lewat pointer, yakni variabel yang menyimpan alamat memori. Topik ini membahas cara kedua wilayah bekerja, perbedaan masa hidup datanya, dan alasan heap tersedia meskipun stack sudah ada. Setelah topik ini dipahami, keputusan kapan menggunakan malloc dan kapan cukup dengan variabel lokal dapat diambil dengan dasar yang jelas.

Dua wilayah memori sebuah program#

Ketika program C dijalankan, sistem operasi menyiapkan ruang memori yang terbagi atas beberapa wilayah dengan tugas berbeda. Dua wilayah yang paling sering bersentuhan dengan kode sehari-hari adalah stack dan heap. Stack menyimpan parameter, variabel lokal, dan alamat kembali setiap pemanggilan fungsi, sedangkan heap adalah kolam memori besar yang blok-bloknya dipesan dan dilepas secara manual lewat fungsi keluarga malloc. Keduanya sama-sama berada di RAM, tetapi cara pemesanan dan aturan masa hidupnya berbeda total.

Diagram berikut menunjukkan susunan wilayah tersebut pada sistem umum. Kode mesin dan data global menempati posisi yang tetap sejak program dimuat, sedangkan stack dan heap tumbuh saling mendekat dari dua arah berlawanan. Tata letak persisnya ditentukan sistem operasi dan dapat berbeda antarplatform, tetapi pembagian tugasnya konsisten.

Kode mesin (text)Data global dan staticHeap: blok malloc dan freeRuang kosongStack: frame fungsitumbuh ke atastumbuh ke bawahStack dan heap tumbuh dari dua arah berlawanan menuju ruang kosong di antara keduanya.
Tata letak memori sebuah program C pada sistem umum

Pemisahan ini menghasilkan dua gaya pengelolaan yang dipakai sepanjang modul. Memori stack dikelola otomatis oleh program, sehingga variabel lokal lahir dan mati mengikuti alur pemanggilan fungsi tanpa satu perintah pembersihan pun. Memori heap dikelola oleh pengembang, sehingga blok yang sudah dipesan tetap ada sampai free dipanggil, kapan pun itu.

Stack: masa hidup sebatas pemanggilan fungsi#

Stack bekerja dengan prinsip tumpukan yang sederhana. Setiap pemanggilan fungsi menaruh satu stack frame berisi parameter dan variabel lokal di puncak tumpukan, dan frame itu dibuang secara utuh saat fungsi kembali. Karena pengelolaannya otomatis, tidak ada fungsi khusus yang perlu dipanggil untuk membersihkannya. Harga praktisnya, data pada stack tidak dapat bertahan melebihi umur fungsi pemiliknya.

Perbedaan masa hidup itu dapat dibuktikan dengan program berikut. Dua penghitung ditambah pada setiap putaran perulangan, tetapi yang pertama adalah variabel lokal milik fungsi catat_stack, sedangkan yang kedua berada pada blok heap yang dipesan lewat malloc dan diakses lewat pointer.

c masa-hidup.c
#include <stdio.h>
#include <stdlib.h>

void catat_stack(void)
{
    int hitungan = 0;          /* dibuat baru pada setiap pemanggilan */
    hitungan = hitungan + 1;
    printf("stack : hitungan = %d\n", hitungan);
}

int main(void)
{
    int *hitungan = malloc(sizeof(int));
    if (hitungan == NULL) {
        return 1;
    }
    *hitungan = 0;

    for (int i = 0; i < 3; i = i + 1) {
        catat_stack();
        *hitungan = *hitungan + 1;
        printf("heap  : hitungan = %d\n", *hitungan);
    }

    free(hitungan);
    return 0;
}
text Keluaran
stack : hitungan = 1
heap  : hitungan = 1
stack : hitungan = 1
heap  : hitungan = 2
stack : hitungan = 1
heap  : hitungan = 3

Keluaran memperlihatkan perbedaannya dengan jelas. Variabel hitungan di dalam catat_stack selalu berawal dari 0 karena frame barunya dibuat ulang pada setiap pemanggilan, sedangkan hitungan pada heap terus bertambah karena bloknya sama dan tetap hidup sepanjang tiga putaran. Blok itu baru benar-benar dilepas ketika free dipanggil di akhir main, bukan ketika satu putaran perulangan selesai.

Perputaran frame tersebut dapat diamati secara langsung pada demo berikut. Tekan Panggil fungsi beberapa kali untuk menumpuk frame, lalu tekan Kembali dari fungsi dan amati frame paling atas hilang bersama variabel lokalnya. Bandingkan dengan blok heap yang tetap terpasang sampai tombol Bebaskan heap terakhir ditekan.

Heap: memori yang dikendalikan lewat malloc dan free#

Heap adalah wilayah yang blok-bloknya dipesan saat program berjalan, bukan saat kode ditulis. Fungsi malloc mengembalikan alamat awal blok sebesar yang diminta, dan alamat itulah yang disimpan pada pointer. Selama alamatnya masih tersimpan dan free belum dipanggil, blok tetap milik program tanpa memandang fungsi mana yang sedang berjalan. Sifat inilah yang membedakan heap dari stack yang masa hidupnya terikat pemanggilan.

Letak kedua wilayah dapat dibuktikan dengan membandingkan alamatnya sebagai bilangan. Tipe bilangan bulat uintptr_t dari header stdint.h mampu menampung nilai pointer, sehingga dua alamat dapat dibandingkan dengan aman tanpa membandingkan pointer mentah secara langsung.

c alamat-wilayah.c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

int main(void)
{
    int lokal = 42;
    int *dinamis = malloc(sizeof(int));
    if (dinamis == NULL) {
        return 1;
    }
    *dinamis = 42;

    printf("Nilai keduanya sama       : %d\n", lokal == *dinamis);
    printf("Alamat stack lebih tinggi : %d\n", (uintptr_t)&lokal > (uintptr_t)dinamis);
    printf("Ukuran satu int           : %zu byte\n", sizeof(int));
    free(dinamis);
    return 0;
}
text Keluaran
Nilai keduanya sama       : 1
Alamat stack lebih tinggi : 1
Ukuran satu int           : 4 byte

Kedua nilai sama-sama 42, tetapi alamat keduanya berbeda wilayah. Angka 1 pada baris kedua berarti alamat variabel lokal berada di atas alamat blok heap. Hasil ini sejalan dengan diagram tata letak, yaitu stack menumpuk dari ujung atas ruang memori, sedangkan blok heap diambil dari kolam yang dikelola pustaka standar. Urutan ini bukan jaminan standar C, tetapi pada satu sistem yang sama polanya stabil dan cukup untuk menegaskan bahwa keduanya memang wilayah berbeda.

Kapan heap diperlukan#

Sebagian besar kebutuhan sehari-hari cukup dilayani stack, sehingga keputusan menggunakan heap sebaiknya selalu punya alasan konkret. Tabel berikut merangkum perbandingan kedua wilayah dari sisi yang paling berpengaruh pada desain program.

AspekStackHeap
Cara memesandeklarasi variabel lokalpanggilan malloc, calloc, atau realloc
Masa hidupsebatas pemanggilan fungsisampai free dipanggil
Ukuran maksimumdibatasi sistem, umumnya hanya beberapa MBmengikuti memori yang tersedia
Ukuran saat memesanharus tetap pada saat kompilasiboleh baru diketahui saat runtime
Pembebasanotomatis saat fungsi kembalimanual lewat free

Tiga kondisi biasanya menuntut heap. Pertama, ukuran data baru diketahui ketika program berjalan, misalnya jumlah nilai yang berasal dari masukan. Kedua, data harus hidup lebih lama daripada fungsi yang membuatnya, seperti hasil perhitungan yang dikembalikan kepada pemanggil. Ketiga, data terlalu besar untuk ditumpuk di stack, misalnya matriks dengan ribuan baris. Di luar ketiga kondisi tersebut, variabel lokal dan array biasa lebih aman karena pembebasannya tidak mungkin terlupa.

Kesalahan umum#

Kesalahan pada pemilihan wilayah memori umumnya tidak terdeteksi saat kompilasi dan baru muncul sebagai perilaku aneh saat program berjalan, sehingga pencegahannya bergantung pada kebiasaan yang benar. Tiga kesalahan berikut paling sering ditemui.

  • Mengembalikan alamat variabel lokal dari fungsi. Frame fungsinya sudah dibuang ketika pemanggil menerima alamat tersebut, sehingga hasilnya adalah pointer yang menunjuk memori tak sah. Kembalikan nilainya secara langsung, gunakan parameter penunjuk keluaran, atau alokasikan blok heap yang memang bertahan.
  • Mengira blok heap ikut dibersihkan saat fungsi kembali. Yang otomatis hilang hanyalah frame stack, sedangkan blok malloc tetap terpasang sampai free dipanggil. Anggapan keliru inilah asal kebocoran memori, dan setiap malloc sebaiknya dipasangkan dengan free yang pasti terjangkau.
  • Mengabaikan batas ukuran stack. Array lokal berukuran jutaan elemen atau rekursi yang terlalu dalam dapat menghabiskan stack dan mengakhiri program dengan error segmentasi. Pindahkan data sebesar itu ke heap dan jaga kedalaman rekursi tetap terkendali.

Ringkasan#

  • Stack menyimpan variabel lokal dan frame pemanggilan dengan masa hidup otomatis sebatas fungsi pemiliknya.
  • Heap menyimpan blok yang dipesan lewat keluarga malloc, dan blok bertahan sampai free dipanggil.
  • Heap diperlukan ketika ukuran data baru diketahui saat runtime, data harus hidup melewati fungsi pembuatnya, atau ukurannya terlalu besar untuk stack.
  • Alamat stack dan heap berasal dari wilayah berbeda, dan perbandingannya dapat dilakukan melalui tipe bilangan seperti uintptr_t.

Referensi#