Bagaimana memahami memori program Anda

Saat membuat kode dalam bahasa seperti C atau C ++, Anda dapat berinteraksi dengan memori Anda dengan cara yang lebih rendah. Terkadang hal ini menciptakan banyak masalah yang tidak Anda dapatkan sebelumnya: segfault . Kesalahan ini agak mengganggu, dan dapat menyebabkan banyak masalah bagi Anda. Mereka sering kali merupakan indikator bahwa Anda menggunakan memori yang tidak boleh Anda gunakan.

Salah satu masalah paling umum adalah mengakses memori yang telah dibebaskan. Ini adalah memori yang Anda keluarkan dengan baik free, atau memori yang telah dilepaskan oleh program Anda secara otomatis, misalnya dari tumpukan.

Memahami semua ini sangat sederhana dan pasti akan membuat program Anda lebih baik dan dengan cara yang lebih cerdas.

Bagaimana memori dibagi?

Memori dibagi dalam beberapa segmen. Dua yang paling penting, untuk posting ini, adalah tumpukan dan heap . Tumpukan adalah tempat penyisipan berurutan sedangkan heap semuanya acak - Anda mengalokasikan memori di mana pun Anda bisa.

Stack memory memiliki sekumpulan cara dan operasi untuk pekerjaannya. Di sinilah beberapa informasi register prosesor Anda disimpan. Dan di situlah informasi yang relevan tentang program Anda pergi - fungsi mana yang dipanggil, variabel apa yang Anda buat, dan beberapa informasi lainnya. Memori ini juga dikelola oleh program dan bukan oleh pengembang.

Heap sering kali digunakan untuk mengalokasikan memori dalam jumlah besar yang seharusnya ada selama yang diinginkan developer. Yang mengatakan, itu tugas pengembang untuk mengontrol penggunaan memori di heap . Saat membuat program yang kompleks, Anda sering kali perlu mengalokasikan sebagian besar memori, dan di sanalah Anda menggunakan heap. Kami menyebutnya Memori Dinamis .

Anda menempatkan sesuatu di heap setiap kali Anda menggunakan mallocuntuk mengalokasikan memori untuk sesuatu. Panggilan lain yang mirip int i;adalah memori tumpukan. Mengetahui hal ini sangat penting agar Anda dapat dengan mudah menemukan kesalahan dalam program Anda dan selanjutnya meningkatkan pencarian kesalahan Segfault Anda.

Memahami tumpukan

Meskipun Anda mungkin tidak mengetahuinya, program Anda secara konstan mengalokasikan memori stack agar berfungsi. Setiap variabel lokal dan setiap fungsi yang Anda panggil pergi ke sana. Dengan ini, Anda dapat melakukan banyak hal - kebanyakan adalah hal-hal yang tidak Anda inginkan terjadi - seperti buffer overflows dan mengakses memori yang salah.

Jadi, bagaimana cara kerjanya?

Tumpukan adalah struktur data LIFO (Last-In-First-Out). Anda dapat melihatnya sebagai sekotak buku yang pas - buku terakhir yang Anda tempatkan adalah yang pertama Anda keluarkan. Dengan menggunakan struktur ini, program dapat dengan mudah mengelola semua operasi dan cakupannya dengan menggunakan dua operasi sederhana: push dan pop .

Keduanya melakukan kebalikan dari satu sama lain. Dorong memasukkan nilai ke atas tumpukan. Pop mengambil nilai tertinggi darinya.

Untuk melacak tempat memori saat ini, ada register prosesor khusus yang disebut Stack Pointer . Setiap kali Anda perlu menyimpan sesuatu - seperti variabel atau alamat pengirim dari suatu fungsi - itu mendorong dan memindahkan penunjuk tumpukan ke atas. Setiap kali Anda keluar dari suatu fungsi, itu memunculkan semuanya dari penunjuk tumpukan hingga alamat pengembalian yang disimpan dari fungsi tersebut. Itu mudah!

Untuk menguji apakah Anda mengerti, mari gunakan contoh berikut (coba dan temukan bugnya saja ☺️):

Jika Anda menjalankannya, program akan segfault. Mengapa ini terjadi? Semuanya terlihat di tempatnya! Kecuali tentang… tumpukan.

Saat kita memanggil fungsinya createArray, tumpukan:

  • menyimpan alamat pengirim,
  • membuat arrdalam memori tumpukan dan mengembalikannya (array hanyalah penunjuk ke lokasi memori dengan informasinya)
  • tapi karena kami tidak menggunakannya, mallocitu disimpan dalam memori tumpukan.

Setelah kami mengembalikan penunjuk, karena kami tidak memiliki kontrol apa pun atas operasi tumpukan, program mengeluarkan info dari tumpukan dan menggunakannya sesuai kebutuhan. Ketika kami mencoba untuk mengisi array setelah kami kembali dari fungsinya, kami merusak memori - membuat program segfault.

Memahami heap

Berlawanan dengan tumpukan, heap adalah yang Anda gunakan saat Anda menginginkan sesuatu ada untuk beberapa waktu secara independen dari fungsi dan cakupan. Untuk menggunakan memori ini, stdlib bahasa C sangat bagus karena ia membawa dua fungsi yang mengagumkan: mallocdan free.

Malloc (alokasi memori) meminta sistem untuk jumlah memori yang diminta, dan mengembalikan sebuah pointer ke alamat awal. Free memberi tahu sistem bahwa memori yang kami minta tidak lagi diperlukan dan dapat digunakan untuk tugas lain. Terlihat sangat sederhana - selama Anda menghindari kesalahan.

Sistem tidak dapat mengganti permintaan pengembang. Jadi tergantung kita, manusia, untuk mengelolanya dengan dua fungsi di atas. Ini membuka pintu untuk satu kesalahan manusia: Kebocoran Memori.

Kebocoran Memori adalah memori yang diminta oleh pengguna yang tidak pernah dibebaskan - ketika program berakhir atau petunjuk ke lokasinya hilang. Hal ini membuat program menggunakan lebih banyak memori daripada yang seharusnya. Untuk menghindari hal ini, setiap kali kita tidak membutuhkan elemen alokasi heap lagi, kita membebaskannya.

Pada gambar di atas, cara buruk tidak pernah membebaskan memori yang kita gunakan. Ini akhirnya membuang 20 * 4 byte (ukuran int dalam 64-bit) = 80 byte. Ini mungkin tidak terlihat terlalu banyak, tapi bayangkan tidak melakukan ini dalam program raksasa. Kita bisa menghabiskan banyak gigabyte!

Mengelola memori heap Anda penting untuk membuat memori program Anda efisien. Tetapi Anda juga harus berhati-hati dalam menggunakannya. Sama seperti dalam memori tumpukan, setelah memori dibebaskan, mengakses atau menggunakannya dapat menyebabkan Anda segfault.

Bonus: Struktur dan heap

Salah satu kesalahan umum saat menggunakan struct adalah membebaskan struct. Ini bagus, selama kita tidak mengalokasikan memori ke pointer di dalam struct. Jika memori dialokasikan ke pointer di dalam struct, pertama-tama kita perlu membebaskannya. Lalu kita bisa membebaskan seluruh struct.

Bagaimana saya mengatasi masalah kebocoran memori saya

Sebagian besar waktu ketika saya memprogram dalam C saya menggunakan struct. Oleh karena itu saya selalu memiliki dua fungsi wajib untuk digunakan dengan struct saya: konstruktor dan destruktor .

Kedua fungsi ini adalah satu-satunya tempat saya menggunakan mallocs dan frees di struct. Ini membuatnya sangat sederhana dan mudah untuk menyelesaikan kebocoran memori saya.

(Jika Anda ingin tahu lebih banyak tentang membuat kode lebih mudah dibaca, periksa posting saya tentang abstraksi).

Alat manajemen memori yang hebat - Valgrind

Sulit untuk mengelola memori Anda dan memastikan bahwa Anda menangani semuanya dengan benar. Alat hebat untuk memvalidasi apakah program Anda berfungsi dengan benar adalah Valgrind. Alat ini memvalidasi program Anda, memberi tahu Anda berapa banyak memori yang Anda alokasikan, berapa banyak yang dibebaskan, jika Anda mencoba menulis di area memori yang salah ... Menggunakannya adalah cara yang bagus untuk memvalidasi apakah semuanya baik-baik saja, dan seseorang harus menggunakannya untuk menghindari gangguan keamanan.

Jangan lupa ikuti saya!

Selain memposting di sini di Medium, saya juga di Twitter.

Jika Anda memiliki pertanyaan atau saran, jangan ragu untuk menghubungi saya.