Seberapa jauh kebocoran memori bisa terjadi?

118

Saya sering mengalami kebocoran memori. Biasanya saat aku malloc-ing seperti tidak ada hari esok, atau tergantungFILE * seperti cucian kotor. Saya biasanya berasumsi (baca: berharap dengan putus asa) bahwa semua memori dibersihkan setidaknya ketika program berakhir. Adakah situasi di mana memori yang bocor tidak akan dikumpulkan saat program dihentikan, atau macet?

Jika jawabannya sangat bervariasi dari satu bahasa ke bahasa lain, maka mari kita fokus pada C (++).

Perhatikan penggunaan hiperbolik frasa, 'seperti tidak ada hari esok', dan 'menjuntai ... seperti cucian kotor'. Tidak aman * mallocdapat melukai orang yang Anda cintai. Juga, harap berhati-hati dengan cucian kotor.

DilithiumMatrix
sumber
3
Jika Anda menjalankan OS "modern" seperti Linux atau Windows, maka OS itu sendiri akan menyelesaikan semua memori yang belum dirilis saat program dihentikan.
Oliver Charlesworth
60
Alih-alih maloc-ing seperti tidak ada hari esok, cobalah berpura-pura ada hari esok dan catat ingatan Anda!
William Pursell
8
@WilliamPursell ah, jadi maksudmu seseorang harus callocseperti tidak ada hari esok. Luar biasa.
DilithiumMatrix
8
"Jika jawabannya sangat bervariasi dari satu bahasa ke bahasa lain, maka mari fokus pada c (++)." c dan c ++ bukan bahasa yang sama!
Johnsyweb
11
@zhermes: Komentar tentang C dan C ++ menjadi bahasa yang berbeda menyembunyikan lebih dari yang Anda kira ... Di C ++ Anda lebih suka memanfaatkan objek dengan durasi penyimpanan otomatis, ikuti idiom RAII ... Anda membiarkan objek ini menjaga memori manajemen untuk Anda.
LihO

Jawaban:

111

Tidak. Sistem operasi membebaskan semua sumber daya yang dimiliki oleh proses ketika mereka keluar.

Ini berlaku untuk semua sumber daya yang dikelola sistem operasi: memori, file terbuka, koneksi jaringan, pegangan jendela ...

Meskipun demikian, jika program berjalan pada sistem tertanam tanpa sistem operasi, atau dengan sistem operasi yang sangat sederhana atau bermasalah, memori mungkin tidak dapat digunakan hingga boot ulang. Tetapi jika Anda berada dalam situasi itu, Anda mungkin tidak akan menanyakan pertanyaan ini.

Sistem operasi mungkin membutuhkan waktu lama untuk membebaskan sumber daya tertentu. Misalnya port TCP yang digunakan server jaringan untuk menerima koneksi mungkin memerlukan beberapa menit untuk menjadi gratis, meskipun ditutup dengan benar oleh program. Program jaringan juga dapat menyimpan sumber daya jarak jauh seperti objek database. Sistem jarak jauh harus membebaskan sumber daya tersebut ketika koneksi jaringan terputus, tetapi mungkin memakan waktu lebih lama daripada sistem operasi lokal.

Joni
sumber
5
Paradigma umum dalam RTOS adalah proses tunggal, model beberapa utas, dan tidak ada perlindungan memori antara 'tugas'. Biasanya ada satu tumpukan. Ini pasti cara kerja VxWorks - dan mungkin masih bekerja.
marko
29
Perhatikan bahwa tidak semua sumber daya dapat dibebaskan oleh sistem operasi. Koneksi jaringan, transaksi database, dll, tidak menutupnya secara eksplisit dapat menyebabkan beberapa hasil yang tidak diinginkan. Tidak menutup koneksi jaringan dapat menyebabkan server berpikir Anda masih aktif untuk waktu yang tidak ditentukan, dan untuk server yang membatasi jumlah koneksi aktif, dapat menyebabkan penolakan layanan secara tidak sengaja. Tidak menutup transaksi database dapat menyebabkan Anda kehilangan data yang tidak terikat.
Lie Ryan
1
@ Marko: Versi terbaru vxWorks sekarang mendukung RTP (proses waktu nyata) yang mendukung perlindungan memori.
Xavier T.
20
"Sistem operasi membebaskan semua sumber daya yang dipegang oleh proses saat mereka keluar." Tidak sepenuhnya benar. Misalnya, di (setidaknya) Linux, semaphore SysV dan objek IPC lainnya tidak dibersihkan saat keluar dari proses. Itulah mengapa ada ipcrmpembersihan manual, linux.die.net/man/8/ipcrm .
sleske
7
Juga, jika sebuah objek memiliki file sementara yang dipelihara, itu jelas tidak akan dibersihkan setelahnya.
Mooing Duck
47

Standar C tidak menentukan bahwa memori yang dialokasikan oleh mallocdilepaskan saat program berakhir. Ini dilakukan oleh sistem operasi dan tidak semua OS (biasanya ini berada di dunia tertanam) melepaskan memori saat program berakhir.

ouah
sumber
20
Itu kurang lebih karena standar C berbicara tentang program C, bukan sistem operasi tempat C berjalan ...
vonbrand
5
@vonbrand Standar C bisa memiliki paragraf yang mengatakan bahwa ketika mainmengembalikan semua memori yang dialokasikan oleh mallocdilepaskan. Misalnya dikatakan bahwa semua file yang terbuka ditutup sebelum program dihentikan. Untuk memori yang dialokasikan saya malloc, itu hanya tidak ditentukan. Sekarang tentu saja kalimat saya tentang OS menjelaskan apa yang biasanya dilakukan bukan apa yang ditentukan Standar, karena tidak menentukan apa pun tentang ini.
ouah
Izinkan saya mengoreksi komentar saya: Standar berbicara tentang C, bukan tentang bagaimana program dimulai dan dihentikan. Anda bisa menulis program C yang berjalan tanpa OS. Dalam hal ini tidak ada yang akan melakukan pembersihan. Standar sangat sengaja tidak menentukan apa pun kecuali diperlukan, agar tidak membatasi penggunaan tanpa perlu.
vonbrand
2
@ouah: " ketika main kembali ...". Itu asumsi. Kita harus mempertimbangkan " jika pengembalian utama ...". std::atexitjuga mempertimbangkan penghentian program melalui std::exit, dan kemudian ada juga std::abortdan (khusus C ++) std::terminate.
MSalters
@ouah: Jika itu dimasukkan, atexittidak akan bisa digunakan. :-)
R .. GitHub STOP HELPING ICE
28

Karena semua jawaban telah mencakup sebagian besar aspek pertanyaan Anda tentang OS modern, tetapi secara historis, ada satu yang perlu disebutkan jika Anda pernah memprogram di dunia DOS. Terminant dan Stay Resident (TSR) biasanya akan mengembalikan kontrol ke sistem tetapi akan berada di memori yang dapat dihidupkan kembali oleh interupsi perangkat lunak / perangkat keras. Itu adalah hal yang normal untuk melihat pesan seperti "kehabisan memori! Coba bongkar beberapa TSR Anda" saat bekerja pada OS ini.

Jadi secara teknis program dihentikan , tetapi karena masih berada di memori, kebocoran memori apa pun tidak akan dilepaskan kecuali Anda membongkar program.

Jadi Anda dapat menganggap ini sebagai kasus lain selain OS yang tidak mengambil kembali memori baik karena bermasalah atau karena OS yang disematkan dirancang untuk melakukannya.

Saya ingat satu contoh lagi. Sistem Kontrol Informasi Pelanggan (CICS), server transaksi yang dijalankan terutama pada mainframe IBM bersifat pseudo-percakapan. Ketika dijalankan, itu memproses data yang dimasukkan pengguna, menghasilkan set data lain untuk pengguna, mentransfer ke node terminal pengguna dan berakhir. Saat mengaktifkan kunci perhatian, itu kembali untuk memproses kumpulan data lain. Karena cara perilakunya, secara teknis lagi, OS tidak akan mengambil kembali memori dari Program CICS yang dihentikan, kecuali Anda mendaur ulang server transaksi CICS.

Abhijit
sumber
Itu sangat menarik, terima kasih atas catatan sejarahnya! Tahukah Anda jika paradigma itu disebabkan oleh membebaskan memori yang terlalu mahal secara komputasi jika tidak diperlukan? Atau apakah alternatifnya belum pernah terpikirkan?
DilithiumMatrix
1
@zhermes: Secara komputasi tidak mungkin, karena DOS tidak melacak alokasi memori untuk TSR. Secara definisi: tujuannya adalah untuk Tetap Tinggal . Jika Anda ingin TSR membebaskan sebagian tetapi tidak semua memori, terserah Anda untuk memutuskan apa yang akan dibebaskan.
MSalters
2
@zhermes: DOS (seperti CP / M, pendahulunya) bukanlah apa yang Anda sebut sistem operasi dalam pengertian modern. Itu benar-benar hanya kumpulan utilitas I / O yang dapat dipanggil dengan cara standar yang dibundel dengan prosesor perintah yang memungkinkan Anda menjalankan satu program dalam satu waktu. Tidak ada gagasan tentang proses, dan memori tidak virtual maupun dilindungi. TSR adalah peretasan berguna yang dapat memberi tahu sistem bahwa mereka menggunakan hingga 64K ruang dan akan menghubungkan diri mereka sendiri ke dalam interupsi sehingga mereka akan dipanggil.
Blrfl
8

Seperti yang dikatakan orang lain, sebagian besar sistem operasi akan mengklaim kembali memori yang dialokasikan setelah penghentian proses (dan mungkin sumber daya lain seperti soket jaringan, pegangan file, dll).

Karena itu, memori mungkin bukan satu-satunya hal yang perlu Anda khawatirkan saat berurusan dengan new / delete (bukan malloc mentah / gratis). Memori yang dialokasikan di new mungkin akan direklamasi, tetapi hal-hal yang mungkin dilakukan di destruktor objek tidak akan terjadi. Mungkin destruktor dari beberapa kelas menulis nilai sentinel ke dalam file setelah penghancuran. Jika proses berhenti begitu saja, pegangan file mungkin akan dihapus dan memori diambil kembali, tetapi nilai sentinel itu tidak akan ditulis.

Moral dari cerita, selalu bersihkan dirimu sendiri. Jangan biarkan hal-hal menjuntai. Jangan mengandalkan pembersihan OS setelah Anda. Bersihkan diri sendiri.

Andre Kostur
sumber
'Jangan mengandalkan pembersihan OS setelah Anda. Bersihkan dirimu sendiri. ' Ini sering kali imp ... 'sangat, sangat sulit' dengan aplikasi multithread yang kompleks. Kebocoran sebenarnya, di mana semua referensi ke sumber daya telah hilang, itu buruk. Mengizinkan OS untuk membersihkan alih-alih merilis referensi secara eksplisit tidak selalu buruk dan seringkali merupakan satu-satunya jalan yang masuk akal untuk diambil.
Martin James
1
Di C ++, destruktor akan dipanggil saat penghentian program (kecuali beberapa kill -9kipas yang kurang terang muncul ...)
vonbrand
@vonbrand Benar, tetapi jika kita berbicara tentang kebocoran dengan objek dinamis, penghancur tersebut tidak akan terjadi. Objek yang keluar dari ruang lingkup adalah penunjuk mentah, dan penghancurnya adalah tanpa operasi. (Tentu saja, lihat objek RAII untuk mengurangi masalah ini ...)
Andre Kostur
1
Masalah dengan RAII adalah bahwa RAII bersikeras untuk melepaskan objek saat keluar dari proses yang sebenarnya tidak penting untuk dihilangkan. Koneksi DB Anda ingin berhati-hati, tetapi memori umum paling baik dibersihkan oleh OS (ini melakukan pekerjaan yang jauh lebih baik). Masalahnya memanifestasikan dirinya sebagai program yang benar-benar membutuhkan waktu lama untuk keluar begitu jumlah memori keluar naik. Ini juga tidak sepele untuk dipecahkan…
Donal Fellows
@vonbrand: Tidak sesederhana itu. std::exitakan memanggil dokter, std::aborttidak akan, pengecualian yang tidak tertangkap mungkin.
MSalters
7

Ini lebih cenderung bergantung pada sistem operasi daripada bahasa. Pada akhirnya program apa pun dalam bahasa apa pun akan mendapatkan memorinya dari sistem operasi.

Saya belum pernah mendengar sistem operasi yang tidak mendaur ulang memori saat program keluar / macet. Jadi, jika program Anda memiliki batas atas pada memori yang perlu dialokasikan, maka mengalokasikan dan tidak pernah membebaskan adalah hal yang wajar.

john
sumber
Bisakah Anda mengacaukan gambar memori kernel dalam kasus OS yang sederhana? .. Seperti, sistem operasi itu bahkan tanpa multitasking.
ulidtko
@ulidtko, ini akan mengacaukan segalanya. Jika program saya membutuhkan katakanlah 1GiB sesekali, dan mengambilnya selama durasi itu, itu berarti menolak penggunaan sumber daya tersebut kepada orang lain bahkan saat tidak menggunakannya. Itu mungkin penting hari ini, atau tidak. Tetapi lingkungan akan berubah secara radikal. Terjamin.
vonbrand
@vonbrand Penggunaan 1GiB yang jarang tidak menjadi masalah secara normal (selama Anda memiliki banyak memori fisik) karena sistem operasi modern dapat menghilangkan bit-bit yang saat ini tidak aktif. Masalahnya muncul ketika Anda memiliki lebih banyak memori virtual dalam penggunaan aktif daripada Anda memiliki memori fisik untuk menyimpannya.
Donal Fellows
5

Jika program pernah berubah menjadi komponen dinamis ("plugin") yang dimuat ke dalam ruang alamat program lain, itu akan merepotkan, bahkan pada sistem operasi dengan manajemen memori yang rapi. Kami bahkan tidak perlu memikirkan tentang kode yang di-porting ke sistem yang kurang mampu.

Di sisi lain, melepaskan semua memori bisa memengaruhi kinerja pembersihan program.

Satu program yang sedang saya kerjakan, kasus uji tertentu memerlukan waktu 30 detik atau lebih untuk keluar dari program, karena program tersebut berulang melalui grafik semua memori dinamis dan melepaskannya sepotong demi sepotong.

Solusi yang masuk akal adalah memiliki kemampuan di sana dan menutupinya dengan kasus uji, tetapi mematikannya dalam kode produksi sehingga aplikasi berhenti dengan cepat.

Kaz
sumber
5

Semua sistem operasi yang layak mendapatkan gelar tersebut akan membersihkan kekacauan yang dibuat oleh proses Anda setelah penghentian. Tetapi selalu ada kejadian yang tidak terduga, bagaimana jika aksesnya ditolak dan beberapa programmer yang buruk tidak meramalkan kemungkinan tersebut sehingga tidak mencoba lagi nanti? Selalu lebih aman untuk membersihkan diri Anda sendiri JIKA kebocoran memori adalah misi penting - jika tidak, upaya itu tidak sepadan dengan upaya IMO jika upaya itu mahal.

Sunting: Anda perlu membersihkan kebocoran memori jika berada di tempat yang akan menumpuk, seperti di loop. Kebocoran memori yang saya bicarakan adalah yang menumpuk dalam waktu konstan selama program berlangsung, jika Anda memiliki kebocoran jenis lain, kemungkinan besar akan menjadi masalah serius cepat atau lambat.

Dalam istilah teknis jika kebocoran Anda adalah 'kompleksitas' memori O (1) mereka baik-baik saja dalam banyak kasus, O (logn) sudah tidak menyenangkan (dan dalam beberapa kasus fatal) dan O (N) + tidak dapat ditoleransi.


sumber
3

Memori bersama pada sistem yang sesuai dengan POSIX tetap ada hingga shm_unlink dipanggil atau sistem di-boot ulang.

klearn
sumber
2

Jika Anda memiliki komunikasi antarproses, ini dapat menyebabkan proses lain tidak pernah menyelesaikan dan menghabiskan sumber daya bergantung pada protokolnya.

Sebagai contoh, saya pernah bereksperimen dengan mencetak ke printer PDF di Java ketika saya menghentikan JVM di tengah pekerjaan printer, proses spooling PDF tetap aktif, dan saya harus mematikannya di task manager sebelum saya bisa coba lagi mencetak.

ratchet freak
sumber