Ini adalah sesuatu yang telah menggangguku sejak lama.
Kita semua diajarkan di sekolah (setidaknya, saya dulu) bahwa Anda HARUS membebaskan setiap pointer yang dialokasikan. Saya agak penasaran, tentang biaya sebenarnya dari tidak membebaskan memori. Dalam beberapa kasus yang jelas, seperti ketika malloc
dipanggil di dalam loop atau bagian dari eksekusi thread, sangat penting untuk membebaskan sehingga tidak ada kebocoran memori. Tetapi perhatikan dua contoh berikut:
Pertama, jika saya memiliki kode itu kira-kira seperti ini:
int main()
{
char *a = malloc(1024);
/* Do some arbitrary stuff with 'a' (no alloc functions) */
return 0;
}
Apa hasil sebenarnya di sini? Pemikiran saya adalah bahwa proses tersebut mati dan kemudian ruang tumpukan hilang sehingga tidak ada salahnya melewatkan panggilan untuk free
(namun, saya mengakui pentingnya memilikinya untuk penutupan, perawatan, dan praktik yang baik). Apakah saya benar dalam pemikiran ini?
Kedua, katakanlah saya memiliki program yang sedikit mirip shell. Pengguna dapat mendeklarasikan variabel suka aaa = 123
dan mereka disimpan dalam beberapa struktur data dinamis untuk digunakan nanti. Jelas, tampaknya jelas bahwa Anda akan menggunakan beberapa solusi yang akan memanggil beberapa fungsi alokasi * (hashmap, daftar tertaut, sesuatu seperti itu). Untuk program semacam ini, tidak masuk akal untuk bebas setelah menelepon malloc
karena variabel-variabel ini harus ada setiap saat selama pelaksanaan program dan tidak ada cara yang baik (yang bisa saya lihat) untuk mengimplementasikannya dengan ruang yang dialokasikan secara statis. Apakah desain yang buruk memiliki banyak memori yang dialokasikan tetapi hanya dibebaskan sebagai bagian dari proses berakhir? Jika demikian, apa alternatifnya?
free(a)
tidak benar-benar melakukan apa pun untuk benar-benar membebaskan memori! Itu hanya me-reset beberapa petunjuk dalam implementasi libc dari malloc yang melacak potongan memori yang tersedia di dalam halaman memori mmapped besar (biasanya disebut "heap"). Halaman itu masih akan dibebaskan hanya ketika program Anda berakhir, bukan sebelumnya.Jawaban:
Hampir setiap sistem operasi modern akan memulihkan semua ruang memori yang dialokasikan setelah program keluar. Satu-satunya pengecualian yang dapat saya pikirkan adalah Palm OS di mana penyimpanan statis dan memori runtime program adalah hal yang hampir sama, jadi tidak membebaskan dapat menyebabkan program mengambil lebih banyak penyimpanan. (Saya hanya berspekulasi di sini.)
Jadi secara umum, tidak ada salahnya, kecuali biaya runtime memiliki lebih banyak penyimpanan daripada yang Anda butuhkan. Tentu saja dalam contoh yang Anda berikan, Anda ingin menyimpan memori untuk variabel yang mungkin digunakan sampai dihapus.
Namun, itu dianggap gaya yang baik untuk membebaskan memori segera setelah Anda tidak membutuhkannya lagi, dan untuk membebaskan apa pun yang masih ada saat keluar dari program. Ini lebih merupakan latihan untuk mengetahui memori apa yang Anda gunakan, dan memikirkan apakah Anda masih membutuhkannya. Jika Anda tidak melacak, Anda mungkin mengalami kebocoran memori.
Di sisi lain, peringatan serupa untuk menutup file Anda saat keluar memiliki hasil yang jauh lebih konkret - jika Anda tidak, data yang Anda tulis kepada mereka mungkin tidak akan memerah, atau jika mereka adalah file temp, mereka mungkin tidak terhapus saat Anda selesai. Juga, pegangan basis data harus memiliki transaksi mereka berkomitmen dan kemudian ditutup ketika Anda selesai dengan mereka. Demikian pula, jika Anda menggunakan bahasa berorientasi objek seperti C ++ atau Objective C, tidak membebaskan objek ketika Anda selesai dengan itu akan berarti destruktor tidak akan pernah dipanggil, dan sumber daya apa pun yang bertanggung jawab kelas mungkin tidak dibersihkan.
sumber
Ya Anda benar, contoh Anda tidak membahayakan (setidaknya tidak pada kebanyakan sistem operasi modern). Semua memori yang dialokasikan oleh proses Anda akan dipulihkan oleh sistem operasi setelah proses keluar.
Sumber: Alokasi dan Mitos GC (Peringatan PostScript!)
Yang mengatakan, Anda harus benar-benar mencoba menghindari semua kebocoran memori!
Pertanyaan kedua: desain Anda ok. Jika Anda perlu menyimpan sesuatu sampai aplikasi Anda keluar, maka ok untuk melakukan ini dengan alokasi memori dinamis. Jika Anda tidak tahu ukuran awal yang diperlukan, Anda tidak dapat menggunakan memori yang dialokasikan secara statis.
sumber
=== Bagaimana dengan pemeriksaan di masa depan dan penggunaan kembali kode ? ===
Jika Anda tidak menulis kode untuk membebaskan objek, maka Anda membatasi kode hanya aman untuk digunakan ketika Anda dapat bergantung pada memori yang dibebaskan oleh proses ditutup ... yaitu penggunaan kecil satu kali proyek atau "membuang-buang" [1] proyek) ... di mana Anda tahu kapan prosesnya akan berakhir.
Jika Anda benar- benar menulis kode yang membebaskan () semua memori yang dialokasikan secara dinamis, maka Anda akan memeriksa kode di masa depan dan membiarkan orang lain menggunakannya dalam proyek yang lebih besar.
[1] tentang proyek "membuang". Kode yang digunakan dalam proyek "Membuang" memiliki cara untuk tidak dibuang. Hal berikutnya yang Anda tahu sepuluh tahun telah berlalu dan kode "membuang" Anda masih digunakan).
Saya mendengar cerita tentang beberapa pria yang menulis beberapa kode hanya untuk bersenang-senang agar perangkat kerasnya bekerja lebih baik. Dia berkata " hanya hobi, tidak akan besar dan profesional ". Bertahun-tahun kemudian, banyak orang menggunakan kode "hobinya".
sumber
malloc()
jika itu, dan berakhir jika pointer masih nol, fungsi seperti itu dapat dengan aman digunakan beberapa kali secara sewenang-wenang bahkan jikafree
tidak pernah dipanggil. Saya pikir mungkin ada baiknya untuk membedakan kebocoran memori yang dapat menggunakan jumlah penyimpanan yang tidak terbatas, dibandingkan situasi yang hanya dapat membuang jumlah penyimpanan yang terbatas dan dapat diprediksi.null
jika tidak ada alokasi, dan bukan nol ketika alokasi memang ada, memiliki kode membebaskan alokasi dan mengatur pointer kenull
ketika konteks dihancurkan akan langsung, terutama dibandingkan dengan segala hal lain yang perlu dilakukan untuk memindahkan objek statis ke dalam struktur konteks.Anda benar, tidak ada kerusakan yang terjadi dan lebih cepat keluar begitu saja
Ada berbagai alasan untuk ini:
Semua lingkungan desktop dan server cukup lepaskan seluruh ruang memori saat keluar (). Mereka tidak mengetahui struktur data internal-program seperti tumpukan.
Hampir semua
free()
implementasi tidak pernah mengembalikan memori ke sistem operasi.Lebih penting lagi, itu buang-buang waktu ketika dilakukan tepat sebelum keluar (). Saat keluar, halaman memori dan ruang swap dilepaskan begitu saja. Sebaliknya, serangkaian panggilan gratis () akan membakar waktu CPU dan dapat mengakibatkan operasi paging disk, cache misses, dan penggusuran cache.
Mengenai possiblility kode masa reuse justifing yang kepastian ops gunanya: itu pertimbangan tapi itu bisa dibilang bukan Agile cara. YAGNI!
sumber
Saya sepenuhnya tidak setuju dengan semua orang yang mengatakan OP benar atau tidak ada salahnya.
Semua orang berbicara tentang OS modern dan / atau warisan.
Tetapi bagaimana jika saya berada di lingkungan di mana saya tidak memiliki OS? Di mana tidak ada apa-apa?
Bayangkan sekarang Anda menggunakan interupsi gaya thread dan mengalokasikan memori. Dalam standar C ISO / IEC: 9899 adalah masa pakai memori yang dinyatakan sebagai:
Jadi tidak harus diberikan bahwa lingkungan melakukan pekerjaan yang membebaskan untuk Anda. Kalau tidak, itu akan ditambahkan ke kalimat terakhir: "Atau sampai program berakhir."
Jadi dengan kata lain: Tidak membebaskan memori bukan hanya praktik buruk. Ini menghasilkan kode tidak sesuai portabel dan bukan C. Yang setidaknya dapat dilihat sebagai 'benar, jika yang berikut: [...], didukung oleh lingkungan'.
Tetapi dalam kasus di mana Anda tidak memiliki OS sama sekali, tidak ada yang melakukan pekerjaan untuk Anda (saya tahu umumnya Anda tidak mengalokasikan dan mengalokasikan kembali memori pada sistem embedded, tetapi ada kasus di mana Anda mungkin ingin.)
Jadi secara umum, kata C (yang mana OP ditandai), ini hanya menghasilkan kode yang salah dan tidak portabel.
sumber
Saya biasanya membebaskan setiap blok yang dialokasikan begitu saya yakin bahwa saya sudah selesai dengan itu. Hari ini, titik masuk program saya mungkin
main(int argc, char *argv[])
, tetapi besok mungkinfoo_entry_point(char **args, struct foo *f)
dan diketik sebagai pointer fungsi.Jadi, jika itu terjadi, saya sekarang mengalami kebocoran.
Mengenai pertanyaan kedua Anda, jika program saya mengambil input seperti a = 5, saya akan mengalokasikan ruang untuk a, atau mengalokasikan kembali ruang yang sama pada a = "foo" berikutnya. Ini akan tetap dialokasikan sampai:
Saya tidak dapat memikirkan OS modern yang tidak mendapatkan kembali memori setelah proses keluar. Kemudian lagi, gratis () murah, mengapa tidak membersihkan? Seperti yang orang lain katakan, alat seperti valgrind sangat bagus untuk menemukan kebocoran yang benar-benar perlu Anda khawatirkan. Meskipun blok yang Anda contoh akan dilabeli sebagai 'masih dapat dijangkau', itu hanya suara tambahan di output ketika Anda mencoba untuk memastikan Anda tidak memiliki kebocoran.
Mitos lain adalah " Jika ini dalam main (), saya tidak perlu membebaskannya ", ini tidak benar. Pertimbangkan yang berikut ini:
Jika itu datang sebelum forking / daemonizing (dan secara teori berjalan selamanya), program Anda baru saja bocor ukuran yang tidak ditentukan t 255 kali.
Program yang baik dan ditulis dengan baik harus selalu membersihkannya sendiri. Kosongkan semua memori, siram semua file, tutup semua deskriptor, putuskan tautan semua file sementara, dll. Fungsi pembersihan ini harus dicapai pada saat terminasi normal, atau setelah menerima berbagai jenis sinyal fatal, kecuali jika Anda ingin membiarkan beberapa file diletakkan sehingga Anda dapat mendeteksi crash dan melanjutkan.
Sungguh, berbaik hatilah kepada jiwa miskin yang harus menjaga barang-barang Anda ketika Anda pindah ke hal-hal lain .. serahkan kepada mereka 'valgrind clean' :)
sumber
free() is cheap
kecuali jika Anda memiliki satu miliar struktur data dengan hubungan kompleks yang harus Anda lepaskan satu per satu, melintasi struktur data untuk mencoba melepaskan semuanya mungkin berakhir secara signifikan meningkatkan waktu shutdown Anda, terutama jika setengah dari struktur data itu sudah dihilangkan ke disk, tanpa manfaat apa pun.Tidak apa-apa untuk membiarkan memori tidak dikreasikan ketika Anda keluar; malloc () mengalokasikan memori dari area memori yang disebut "heap", dan heap lengkap dari suatu proses dibebaskan ketika proses keluar.
Yang sedang berkata, salah satu alasan mengapa orang masih bersikeras bahwa itu baik untuk membebaskan semuanya sebelum keluar adalah bahwa memory debugger (mis. Valgrind di Linux) mendeteksi blok yang tidak diberi pembaruan sebagai kebocoran memori, dan jika Anda juga memiliki "kehabisan memori" kebocoran, itu menjadi lebih sulit untuk menemukan mereka jika Anda juga mendapatkan hasil "palsu" pada akhirnya.
sumber
free
padaexit
saat itu dianggap berbahaya.Jika Anda menggunakan memori yang Anda alokasikan, maka Anda tidak melakukan kesalahan. Ini menjadi masalah ketika Anda menulis fungsi (selain utama) yang mengalokasikan memori tanpa membebaskannya, dan tanpa membuatnya tersedia untuk sisa program Anda. Kemudian program Anda terus berjalan dengan memori yang dialokasikan untuk itu, tetapi tidak ada cara menggunakannya. Program Anda dan program lain yang berjalan tidak memiliki memori itu.
Sunting: Ini tidak 100% akurat untuk mengatakan bahwa program lain yang berjalan kekurangan memori itu. Sistem operasi selalu dapat membiarkan mereka menggunakannya dengan mengorbankan program Anda ke memori virtual (
</handwaving>
). Intinya adalah, bahwa jika program Anda membebaskan memori yang tidak digunakan maka swap memori virtual cenderung diperlukan.sumber
Kode ini biasanya akan berfungsi dengan baik, tetapi pertimbangkan masalah penggunaan kembali kode.
Anda mungkin telah menulis beberapa potongan kode yang tidak membebaskan memori yang dialokasikan, itu dijalankan sedemikian rupa sehingga memori kemudian secara otomatis direklamasi. Sepertinya baik-baik saja.
Lalu orang lain menyalin cuplikan Anda ke proyeknya sedemikian rupa sehingga dijalankan seribu kali per detik. Orang itu sekarang memiliki kebocoran memori yang sangat besar dalam programnya. Tidak terlalu bagus secara umum, biasanya fatal untuk aplikasi server.
Penggunaan kembali kode adalah tipikal di perusahaan. Biasanya perusahaan memiliki semua kode yang diproduksi karyawannya dan setiap departemen dapat menggunakan kembali apa pun yang dimiliki perusahaan. Jadi dengan menulis kode "kelihatan polos" seperti itu, Anda berpotensi menimbulkan sakit kepala bagi orang lain. Ini bisa membuat Anda dipecat.
sumber
Program Anda membocorkan memori. Tergantung pada OS Anda, itu mungkin telah pulih.
Sebagian besar sistem operasi desktop modern dapat memulihkan memori yang bocor pada saat proses terminasi, sehingga sangat umum untuk mengabaikan masalah, seperti yang dapat dilihat oleh banyak jawaban lain di sini.)
Tetapi Anda mengandalkan fitur keselamatan yang tidak seharusnya Anda andalkan, dan program (atau fungsi) Anda mungkin berjalan pada sistem di mana perilaku ini menghasilkan kebocoran memori "keras", lain kali.
Anda mungkin berjalan dalam mode kernel, atau pada sistem operasi vintage / tertanam yang tidak menggunakan perlindungan memori sebagai tradeoff. (MMU mengambil ruang mati, perlindungan memori biaya siklus CPU tambahan, dan tidak terlalu banyak meminta dari seorang programmer untuk membersihkan setelah dirinya sendiri).
Anda dapat menggunakan dan menggunakan kembali memori dengan cara apa pun yang Anda suka, tetapi pastikan Anda membatalkan alokasi semua sumber daya sebelum keluar.
sumber
Sebenarnya ada bagian dalam buku teks online OSTEP untuk kursus sarjana dalam sistem operasi yang membahas pertanyaan Anda dengan tepat.
Bagian yang relevan adalah "Lupa Untuk Membebaskan Memori" di bab Memory API di halaman 6 yang memberikan penjelasan berikut:
Kutipan ini dalam konteks memperkenalkan konsep memori virtual. Pada dasarnya pada titik ini dalam buku ini, penulis menjelaskan bahwa salah satu tujuan dari sistem operasi adalah untuk "virtualisasi memori," yaitu, untuk membuat setiap program percaya bahwa ia memiliki akses ke ruang alamat memori yang sangat besar.
Di belakang layar, sistem operasi akan menerjemahkan "alamat virtual" yang dilihat pengguna ke alamat aktual yang menunjuk ke memori fisik.
Namun, berbagi sumber daya seperti memori fisik membutuhkan sistem operasi untuk melacak proses apa yang menggunakannya. Jadi jika suatu proses berakhir, maka itu berada dalam kemampuan dan tujuan desain sistem operasi untuk merebut kembali memori proses sehingga dapat mendistribusikan kembali dan berbagi memori dengan proses lain.
EDIT: Samping yang disebutkan dalam kutipan disalin di bawah ini.
sumber
Tidak ada bahaya nyata dalam tidak membebaskan variabel Anda, tetapi jika Anda menetapkan pointer ke blok memori ke blok memori yang berbeda tanpa membebaskan blok pertama, blok pertama tidak lagi dapat diakses tetapi masih membutuhkan ruang. Inilah yang disebut kebocoran memori, dan jika Anda melakukan ini dengan teratur maka proses Anda akan mulai mengkonsumsi semakin banyak memori, mengambil sumber daya sistem dari proses lain.
Jika proses ini berumur pendek, Anda sering dapat melakukan hal ini karena semua memori yang dialokasikan direklamasi oleh sistem operasi ketika prosesnya selesai, tetapi saya sarankan untuk membiasakan membebaskan semua memori yang tidak perlu Anda gunakan lagi.
sumber
Anda benar dalam hal itu. Dalam program kecil yang sepele di mana variabel harus ada sampai kematian program, tidak ada manfaat nyata untuk membatalkan alokasi memori.
Bahkan, saya pernah terlibat dalam sebuah proyek di mana setiap pelaksanaan program itu sangat kompleks tetapi relatif singkat, dan keputusannya adalah menjaga agar memori tetap dialokasikan dan tidak mengacaukan proyek dengan membuat kesalahan deallocating itu.
Yang sedang berkata, di sebagian besar program ini sebenarnya bukan pilihan, atau dapat menyebabkan Anda kehabisan memori.
sumber
Anda benar, memori secara otomatis dibebaskan ketika proses keluar. Beberapa orang berusaha untuk tidak melakukan pembersihan yang luas ketika proses dihentikan, karena semuanya akan dilepaskan ke sistem operasi. Namun, saat program Anda berjalan Anda harus membebaskan memori yang tidak digunakan. Jika tidak, akhirnya Anda akan kehabisan atau menyebabkan paging yang berlebihan jika perangkat kerja Anda terlalu besar.
sumber
Jika Anda mengembangkan aplikasi dari awal, Anda dapat membuat beberapa pilihan cerdas tentang kapan harus menelepon gratis. Contoh program Anda baik-baik saja: ini mengalokasikan memori, mungkin Anda membuatnya berfungsi selama beberapa detik, dan kemudian ditutup, membebaskan semua sumber daya yang diklaimnya.
Namun, jika Anda menulis hal lain - server / aplikasi yang sudah berjalan lama, atau perpustakaan untuk digunakan oleh orang lain, Anda harus menelepon gratis di semua yang Anda malloc.
Mengabaikan sisi pragmatis sesaat, jauh lebih aman untuk mengikuti pendekatan yang lebih ketat, dan memaksakan diri Anda untuk membebaskan semua yang Anda malloc. Jika Anda tidak terbiasa mengawasi kebocoran memori setiap kali Anda membuat kode, Anda dapat dengan mudah membuat beberapa kebocoran. Jadi dengan kata lain, ya - Anda bisa pergi tanpa itu; harap hati-hati.
sumber
Jika suatu program lupa untuk membebaskan beberapa Megabytes sebelum keluar, sistem operasi akan membebaskan mereka. Tetapi jika program Anda berjalan selama berminggu-minggu pada satu waktu dan satu loop di dalam program lupa untuk membebaskan beberapa byte di setiap iterasi Anda akan memiliki kebocoran memori yang kuat yang akan memakan semua memori yang tersedia di komputer Anda kecuali jika Anda reboot secara teratur basis => bahkan kebocoran memori kecil mungkin buruk jika program ini digunakan untuk tugas besar yang serius bahkan jika awalnya tidak dirancang untuk satu.
sumber
Saya pikir dua contoh Anda sebenarnya hanya satu: yang
free()
seharusnya terjadi hanya pada akhir proses, yang seperti yang Anda tunjukkan tidak berguna karena proses ini berakhir.Dalam contoh kedua Anda, satu-satunya perbedaan adalah bahwa Anda mengizinkan jumlah yang tidak ditentukan
malloc()
, yang dapat menyebabkan kehabisan memori. Satu-satunya cara untuk menangani situasi ini adalah dengan memeriksa kode pengembalianmalloc()
dan bertindak sesuai.sumber