Apa yang BENAR-BENAR terjadi ketika Anda tidak bebas setelah malloc?


538

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 mallocdipanggil 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 = 123dan 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 mallockarena 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?


7
@NTDLS Keajaiban sistem peringkat benar-benar berfungsi sekali: 6 tahun kemudian dan jawaban "lebih pantas" memang naik ke atas.
zxq9

15
Orang-orang di bawah terus mengatakan bahwa OS modern yang baik tidak melakukan pembersihan tetapi bagaimana jika kode tersebut berjalan dalam mode kernel (misalnya, untuk alasan kinerja)? Apakah program mode kernel (di Linux misalnya) di-sandbox? Jika tidak, saya yakin Anda perlu secara manual membebaskan semuanya, saya kira, bahkan sebelum penghentian yang abnormal seperti dengan abort ().
Dr. Person Person II

3
@ Dr.PersonPersonII Ya, kode yang berjalan dalam mode kernel biasanya harus membebaskan semuanya secara manual.
zwol

1
Saya ingin menambahkan yang 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.
Marco Bonelli

1
Free () mungkin, atau mungkin tidak, benar-benar melepaskan memori. Itu mungkin hanya menandai blok sebagai dibebaskan, untuk direklamasi nanti, atau mungkin menghubungkannya ke daftar gratis. Mungkin menggabungkannya ke blok gratis yang berdekatan, atau mungkin membiarkannya untuk dilakukan alokasi selanjutnya. Itu semua detail implementasi.
Jordan Brown

Jawaban:


378

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.


16
Mungkin akan lebih baik untuk menyebutkan bahwa tidak semua orang menggunakan sistem operasi modern, jika seseorang mengambil program Anda (dan itu masih berjalan pada OS yang tidak memulihkan memori) menjalankannya kemudian GG.
user105033

79
Saya benar-benar menganggap jawaban ini salah. Seseorang harus selalu mendelokasi sumber daya setelah satu selesai dengan mereka, baik itu menangani file / memori / mutex. Dengan memiliki kebiasaan itu, seseorang tidak akan membuat kesalahan semacam itu ketika membangun server. Beberapa server diharapkan berjalan 24x7. Dalam kasus tersebut, kebocoran apa pun berarti server Anda pada akhirnya akan kehabisan sumber daya dan hang / crash. Program utilitas singkat, ya kebocoran tidak terlalu buruk. Server apa pun, kebocoran apa pun adalah kematian. Bantulah dirimu sendiri. Bersihkan dirimu. Itu kebiasaan yang baik.
EvilTeach

120
Bagian mana dari "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." apakah Anda menganggap salah?
Paul Tomblin

24
Jika Anda memiliki penyimpanan memori yang Anda butuhkan hingga saat program keluar, dan Anda tidak menjalankan OS primitif, maka membebaskan memori sebelum Anda keluar adalah pilihan gaya, bukan cacat.
Paul Tomblin

30
@ Paul - Hanya setuju dengan EvilTeach, tidak dianggap sebagai gaya yang baik untuk membebaskan memori, itu salah untuk tidak membebaskan memori. Kata-kata Anda membuat ini tampak sama pentingnya dengan mengenakan sapu tangan yang cocok dengan dasi Anda. Sebenarnya itu ada di level memakai celana.
Heath Hunnicutt

110

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!)

Mitos Alokasi 4: Program yang dikumpulkan dari sampah harus selalu membatalkan alokasi semua memori yang mereka alokasikan.

The Truth: Deallocations yang dihilangkan dalam kode yang sering dieksekusi menyebabkan kebocoran yang meningkat. Mereka jarang diterima. tetapi Program yang mempertahankan sebagian besar memori yang dialokasikan hingga keluar dari program sering berkinerja lebih baik tanpa ada penundaan alokasi Malloc jauh lebih mudah diimplementasikan jika tidak ada yang gratis.

Dalam kebanyakan kasus, membatalkan memori sebelum program keluar tidak ada gunanya. OS tetap akan mengklaimnya kembali. Bebas akan menyentuh dan halaman di benda mati; OS tidak mau.

Konsekuensi: Berhati-hatilah dengan "detektor kebocoran" yang menghitung alokasi. Beberapa "kebocoran" bagus!

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.


3
Mungkin karena pertanyaannya, seperti yang saya baca, adalah apa yang sebenarnya terjadi pada memori yang bocor, bukan apakah contoh spesifik ini baik-baik saja. Saya tidak akan memilihnya, karena itu masih jawaban yang bagus.
DevinB

3
Mungkin ada (awal Windows, awal OS Mac), dan mungkin masih, OS yang membutuhkan proses untuk membebaskan memori sebelum keluar jika tidak ruang tidak direklamasi.
Pete Kirkham

Tidak apa-apa, kecuali Anda peduli dengan fragmentasi memori atau kehabisan memori - Anda melakukan ini terlalu banyak dan kinerja aplikasi Anda akan hilang. Selain fakta-fakta sulit, selalu ikuti praktik terbaik & pembangunan kebiasaan baik.
NTDLS

1
Saya punya jawaban yang diterima yang saat ini duduk sekitar -11, jadi dia bahkan tidak dalam menjalankan untuk catatan.
Paul Tomblin

8
Saya pikir itu salah untuk menjelaskan perlunya gratis () ing memori dengan mengatakan "karena detektor kebocoran". Ini seperti mengatakan "Anda harus mengemudi perlahan di jalan bermain karena polisi bisa menunggu Anda dengan kamera kecepatan".
Sebastian Mach

57

=== 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".


8
Diturunkan untuk "proyek kecil". Ada banyak proyek besar yang dengan sengaja tidak membebaskan memori saat keluar karena membuang-buang waktu jika Anda tahu platform target Anda. IMO, contoh yang lebih akurat adalah "proyek terisolasi". Misalnya jika Anda membuat pustaka yang dapat digunakan kembali yang akan disertakan dalam aplikasi lain, tidak ada titik keluar yang terdefinisi dengan baik sehingga Anda tidak boleh membocorkan memori. Untuk aplikasi mandiri, Anda akan selalu tahu persis kapan prosesnya berakhir dan dapat membuat keputusan sadar untuk melepas pembersihan ke OS (yang harus melakukan pemeriksaan dengan cara apa pun).
Dan Bechard

Aplikasi kemarin adalah fungsi pustaka hari ini, dan besok akan ditautkan ke server berumur panjang yang menyebutnya ribuan kali.
Adrian McCarthy

1
@AdrianMcCarthy: Jika suatu fungsi memeriksa apakah pointer statis adalah null, menginisialisasi dengan malloc()jika itu, dan berakhir jika pointer masih nol, fungsi seperti itu dapat dengan aman digunakan beberapa kali secara sewenang-wenang bahkan jika freetidak 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.
supercat

@supercat: Komentar saya berbicara tentang perubahan kode dari waktu ke waktu. Tentu, membocorkan memori dengan jumlah terbatas tidak menjadi masalah. Tetapi suatu hari, seseorang akan ingin mengubah fungsi itu sehingga tidak lagi menggunakan pointer statis. Jika kode tidak memiliki ketentuan untuk dapat melepaskan memori menunjuk-ke, itu akan menjadi perubahan yang sulit (atau, lebih buruk lagi, perubahan itu akan buruk dan Anda akan berakhir dengan kebocoran yang tidak terbatas).
Adrian McCarthy

1
@AdrianMcCarthy: Mengubah kode untuk tidak lagi menggunakan pointer statis kemungkinan akan membutuhkan memindahkan pointer ke semacam "konteks" objek, dan menambahkan kode untuk membuat dan menghancurkan objek tersebut. Jika pointer selalu nulljika tidak ada alokasi, dan bukan nol ketika alokasi memang ada, memiliki kode membebaskan alokasi dan mengatur pointer ke nullketika konteks dihancurkan akan langsung, terutama dibandingkan dengan segala hal lain yang perlu dilakukan untuk memindahkan objek statis ke dalam struktur konteks.
supercat

52

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!


2
Saya pernah bekerja pada sebuah proyek di mana kami menghabiskan waktu singkat mencoba memahami penggunaan memori program (kami diminta untuk mendukungnya, kami tidak menulisnya). Berdasarkan pengalaman saya secara anekdot setuju dengan peluru kedua Anda. Namun, saya ingin mendengar Anda (atau seseorang) memberikan lebih banyak bukti bahwa ini benar.
user106740

3
Sudahlah, temukan jawabannya: stackoverflow.com/questions/1421491/… . Terima kasih!
user106740

@ Naviggiano itu disebut YAGNI.
v.oddou

Prinsip YAGNI bekerja dengan dua cara: Anda tidak perlu mengoptimalkan jalur shutdown. Optimalisasi prematur dan semua itu.
Adrian McCarthy

26

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:

7.20.3 Fungsi manajemen memori

1 Urutan dan kedekatan penyimpanan yang dialokasikan oleh panggilan berturut-turut ke fungsi calloc, malloc, dan realalloc tidak ditentukan. Pointer kembali jika alokasi berhasil selaras sehingga dapat ditugaskan untuk pointer ke semua jenis objek dan kemudian digunakan untuk mengakses objek atau array objek tersebut di ruang yang dialokasikan (sampai ruang secara eksplisit dialokasikan) . Umur objek yang dialokasikan meluas dari alokasi sampai deallokasi. [...]

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.


4
Argumen tandingannya adalah bahwa jika Anda adalah lingkungan yang tertanam, Anda - sebagai pengembang - akan jauh lebih cerewet dalam manajemen memori Anda. Biasanya, ini adalah titik sebenarnya mengalokasikan memori tetap statis sebelumnya daripada memiliki runtime mallocs / reallocs sama sekali.
John Go-Soco

1
@lunarplasma: Walaupun apa yang Anda katakan tidak salah, itu tidak mengubah fakta apa yang dinyatakan oleh standar bahasa, dan semua orang yang bertindak melawan / memajukannya, mungkin dengan akal sehat, menghasilkan kode terbatas. Saya bisa mengerti jika seseorang mengatakan "Saya tidak perlu peduli tentang itu", karena ada cukup banyak kasus di mana itu ok. TETAPI orang setidaknya harus tahu MENGAPA dia tidak perlu peduli. dan terutama tidak menghilangkannya selama pertanyaan tidak terkait dengan kasus khusus itu. Dan karena OP bertanya tentang C secara umum di bawah aspek teoritis (sekolah). Tidak apa-apa mengatakan "Kamu tidak perlu"!
dhein

2
Di sebagian besar lingkungan di mana tidak ada OS, tidak ada cara melalui mana program dapat "berakhir".
supercat

@ supercat: Seperti yang saya tulis sebelumnya: Anda benar tentang hal itu. Tetapi jika seseorang bertanya tentang hal itu sehubungan dengan alasan pengajaran dan aspek sekolah, itu tidak benar untuk mengatakan "Anda tidak perlu berpikir tentang itu karena sebagian besar waktu itu tidak masalah" Kata-kata dan perilaku bahasa definisi diberikan karena suatu alasan, dan hanya karena sebagian besar lingkungan menanganinya untuk Anda, Anda tidak bisa mengatakan tidak perlu peduli. Itu maksudku.
dhein

2
-1 untuk mengutip standar C sementara sebagian besar TIDAK berlaku tanpa adanya sistem operasi, karena tidak ada runtime untuk memberikan fitur mandat standar, terutama mengenai manajemen memori dan fungsi perpustakaan standar (yang juga jelas tidak ada bersama dengan runtime / OS).

23

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 mungkin foo_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:

  1. Pengguna mengetik 'tidak disetel'
  2. Fungsi pembersihan saya dimasukkan, baik melayani sinyal atau pengguna mengetik 'berhenti'

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:

char *t;

for (i=0; i < 255; i++) {
    t = strdup(foo->name);
    let_strtok_eat_away_at(t);
}

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' :)


1
Dan ya, saya pernah punya teman satu tim memberi tahu saya: "Saya tidak pernah perlu menelepon gratis () pada main ()" <shudders>
Tim Post

free() is cheapkecuali 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.
Lie Ryan

3
@ LieRyan Jika Anda memiliki satu miliar, seperti dalam harfiah satu miliar struktur, Anda pasti memiliki masalah lain yang memerlukan tingkat pertimbangan khusus - jauh di luar cakupan jawaban khusus ini :)
Tim Post

13

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.


1
Bukankah Valgrind melakukan pekerjaan yang cukup baik membedakan antara "bocor" dan "masih bisa dijangkau"?
Christoffer

11
-1 untuk "sepenuhnya baik-baik saja" Ini adalah praktik pengkodean yang buruk untuk meninggalkan memori yang dialokasikan tanpa membebaskannya. Jika kode itu diekstraksi ke perpustakaan, maka itu akan menyebabkan memleak di semua tempat.
DevinB

5
+1 untuk memberi kompensasi. Lihat jawaban perusahaan. freepada exitsaat itu dianggap berbahaya.
R .. GitHub BERHENTI MEMBANTU ICE

11

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.


11

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.


2
Mungkin patut dicatat kemungkinan tidak hanya seseorang menyalin cuplikan, tetapi juga kemungkinan program yang ditulis untuk melakukan beberapa tindakan tertentu setelah dimodifikasi untuk melakukannya berulang kali. Dalam kasus seperti itu, akan baik-baik saja jika memori dialokasikan sekali dan kemudian digunakan berulang kali tanpa pernah dibebaskan, tetapi mengalokasikan dan meninggalkan memori untuk setiap tindakan (tanpa membebaskannya) bisa menjadi bencana.
supercat

7

Apa hasil sebenarnya di sini?

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.


5

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:

Dalam beberapa kasus, sepertinya tidak menelepon gratis () masuk akal. Misalnya, program Anda berumur pendek, dan akan segera keluar; dalam hal ini, ketika proses mati, OS akan membersihkan semua halaman yang dialokasikan dan dengan demikian tidak ada kebocoran memori yang terjadi per se. Meskipun ini pasti “berhasil” (lihat halaman samping di halaman 7), mungkin kebiasaan buruk untuk dikembangkan, jadi berhati-hatilah memilih strategi seperti itu

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.

Selain itu, MENGAPA TIDAK MEMORI YANG TERCOCOK SEKALI PROSES ANDA

Ketika Anda menulis program yang berumur pendek, Anda mungkin mengalokasikan beberapa ruang menggunakan malloc(). Program berjalan dan hampir selesai: apakah perlu menelepon free()beberapa kali sebelum keluar? Meskipun tampaknya salah untuk tidak melakukannya, tidak ada memori yang akan "hilang" dalam arti sebenarnya. Alasannya sederhana: sebenarnya ada dua tingkat manajemen memori dalam sistem. Level pertama manajemen memori dilakukan oleh OS, yang membagikan memori untuk diproses saat dijalankan, dan membawanya kembali saat proses keluar (atau mati). Tingkat manajemen kedua adalah dalam setiap proses, misalnya dalam tumpukan ketika Anda menelepon malloc()dan free(). Bahkan jika Anda gagal meneleponfree()(dan dengan demikian membocorkan memori di heap), sistem operasi akan mengambil kembali semua memori dari proses (termasuk halaman-halaman untuk kode, stack, dan, sebagaimana relevan di sini, heap) ketika program selesai berjalan. Tidak peduli bagaimana keadaan tumpukan Anda di ruang alamat Anda, OS mengambil kembali semua halaman tersebut ketika proses mati, sehingga memastikan bahwa tidak ada memori yang hilang terlepas dari kenyataan bahwa Anda tidak membebaskannya.

Dengan demikian, untuk program yang berumur pendek, kebocoran memori sering tidak menyebabkan masalah operasional (meskipun mungkin dianggap buruk). Ketika Anda menulis server yang sudah berjalan lama (seperti server web atau sistem manajemen basis data, yang tidak pernah keluar), memori yang bocor adalah masalah yang jauh lebih besar, dan pada akhirnya akan menyebabkan crash ketika aplikasi kehabisan memori. Dan tentu saja, kebocoran memori adalah masalah yang bahkan lebih besar di dalam satu program tertentu: sistem operasi itu sendiri. Menampilkan kami sekali lagi: mereka yang menulis kode kernel memiliki pekerjaan terberat dari semua ...

dari Halaman 7 dari Memory API bab

Sistem Operasi: Tiga
Buku Mudah Remzi H. Arpaci-Dusseau dan Andrea C. Arpaci-Dusseau Arpaci-Dusseau Buku Maret, 2015 (Versi 0.90)


4

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.


1
Saya ingin mengatakan -1 untuk pernyataan pertama Anda "tidak ada bahaya" kecuali bahwa Anda kemudian memberikan jawaban yang bijaksana tentang mengapa ada bahaya.
DevinB

2
Seperti bahaya pergi itu cukup jinak - Saya akan mengambil kebocoran memori atas segfault setiap hari.
Kyle Cronin

1
Sangat benar, dan kami berdua lebih suka tidak = D
DevinB

2
@KyleCronin Saya lebih suka memiliki segfault daripada kebocoran memori, karena keduanya adalah bug serius dan segfault lebih mudah dideteksi. Terlalu sering kebocoran memori tidak diketahui atau tidak terselesaikan karena "cukup jinak". RAM saya dan saya sepenuh hati tidak setuju.
Dan Bechard

@Dan Sebagai pengembang, tentu saja. Sebagai pengguna, saya akan mengambil kebocoran memori. Saya lebih suka memiliki perangkat lunak yang berfungsi, meskipun dengan memori yang bocor, daripada perangkat lunak yang tidak.
Kyle Cronin

3

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.


2

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.


2

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.


0

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.


-2

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 pengembalian malloc()dan bertindak sesuai.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.