Atau lebih seperti "membuang benda di C ++ benar-benar rumit - saya menghabiskan 20% waktu saya untuk itu, namun, kebocoran memori adalah hal yang biasa"?
Dalam pengalaman pribadi saya di C ++ dan bahkan C, kebocoran memori tidak pernah sulit untuk dihindari. Dengan prosedur pengujian yang waras dan Valgrind, misalnya, setiap kebocoran fisik yang disebabkan oleh panggilan operator new/malloc
tanpa sambungan delete/free
sering terdeteksi dan diperbaiki dengan cepat. Agar adil beberapa kode C besar atau sekolah tua C ++ mungkin sangat mungkin memiliki beberapa kasus tepi yang tidak jelas yang mungkin secara fisik membocorkan beberapa byte memori di sana-sini sebagai akibat dari tidak deleting/freeing
dalam kasus tepi yang terbang di bawah radar pengujian.
Namun sejauh pengamatan praktis, aplikasi terlemah yang saya temui (seperti pada yang mengkonsumsi lebih banyak dan lebih banyak memori semakin lama Anda menjalankannya, meskipun jumlah data yang kami kerjakan tidak bertambah) biasanya tidak ditulis dalam C atau C ++. Saya tidak menemukan hal-hal seperti Linux Kernel atau Unreal Engine atau bahkan kode asli yang digunakan untuk mengimplementasikan Java di antara daftar perangkat lunak bocor yang saya temui.
Jenis perangkat lunak bocor yang paling menonjol yang cenderung saya temui adalah hal-hal seperti applet Flash, seperti game Flash, meskipun mereka menggunakan pengumpulan sampah. Dan itu bukan perbandingan yang adil jika seseorang menyimpulkan sesuatu dari ini karena banyak aplikasi Flash ditulis oleh pengembang pemula yang mungkin kurang memiliki prinsip-prinsip rekayasa suara dan prosedur pengujian (dan juga saya yakin ada profesional yang terampil di luar sana yang bekerja dengan GC yang jangan berjuang dengan perangkat lunak yang bocor), tetapi saya ingin mengatakan banyak hal kepada siapa pun yang berpikir GC mencegah perangkat lunak yang bocor ditulis.
Pointer Menggantung
Sekarang datang dari domain khusus saya, pengalaman, dan sebagai salah satu yang sebagian besar menggunakan C dan C ++ (dan saya berharap manfaat GC akan bervariasi tergantung pada pengalaman dan kebutuhan kami), hal yang paling langsung diselesaikan GC bagi saya bukanlah masalah kebocoran memori praktis tetapi Menggantung akses pointer, dan itu benar-benar bisa menjadi penyelamat dalam skenario misi-kritis.
Sayangnya dalam banyak kasus di mana GC memecahkan apa yang seharusnya menjadi akses pointer menggantung, itu menggantikan kesalahan programmer yang sama dengan kebocoran memori logis.
Jika Anda membayangkan game Flash yang ditulis oleh pembuat kode pemula, ia mungkin menyimpan referensi ke elemen-elemen game dalam beberapa struktur data, membuatnya berbagi kepemilikan atas sumber daya game ini. Sayangnya, katakanlah dia membuat kesalahan di mana dia lupa untuk menghapus elemen-elemen game dari salah satu struktur data setelah maju ke tahap berikutnya, mencegah mereka dibebaskan sampai seluruh game ditutup. Namun, gim ini tetap berfungsi dengan baik karena elemen-elemennya tidak digambar atau memengaruhi interaksi pengguna. Namun demikian, gim ini mulai menggunakan lebih banyak dan lebih banyak memori sementara laju bingkai bekerja sendiri untuk tayangan slide, sementara pemrosesan tersembunyi masih berulang melalui kumpulan elemen tersembunyi dalam gim ini (yang kini telah menjadi sangat besar ukurannya). Ini adalah jenis masalah yang sering saya temui dalam game Flash tersebut.
- Saya telah menemukan orang mengatakan ini tidak dihitung sebagai "kebocoran memori" karena memori masih dibebaskan setelah menutup aplikasi, dan sebagai gantinya mungkin disebut 'kebocoran ruang' atau sesuatu untuk efek ini. Sementara perbedaan seperti itu mungkin berguna untuk mengidentifikasi dan berbicara tentang masalah, saya tidak menemukan perbedaan seperti itu sangat berguna dalam konteks ini jika kita berbicara tentang itu seperti itu tidak bermasalah seperti "kebocoran memori" ketika kita berurusan tujuan praktis untuk memastikan perangkat lunak tidak memonopoli jumlah memori yang konyol semakin lama kita menjalankannya (kecuali kita berbicara sistem operasi yang tidak membebaskan memori proses ketika dihentikan).
Sekarang katakanlah pengembang pemula yang sama menulis game di C ++. Dalam hal itu biasanya hanya akan ada satu struktur data pusat dalam game yang "memiliki" memori sementara yang lain menunjuk ke memori itu. Jika dia membuat kesalahan yang sama, kemungkinannya adalah bahwa, setelah maju ke tahap berikutnya, permainan akan crash sebagai akibat dari mengakses pointer menggantung (atau lebih buruk, melakukan sesuatu selain crash).
Ini adalah jenis trade-off paling cepat yang cenderung saya temui di domain saya paling sering antara GC dan tidak ada GC. Dan saya sebenarnya tidak terlalu peduli dengan GC di domain saya, yang tidak terlalu kritis terhadap misi, karena pergulatan terbesar yang pernah saya miliki dengan perangkat lunak bocor melibatkan penggunaan GC secara serampangan di sebuah tim sebelumnya yang menyebabkan kebocoran yang dijelaskan di atas. .
Dalam domain khusus saya, saya sebenarnya lebih suka perangkat lunak crash atau glitching dalam banyak kasus karena itu setidaknya jauh lebih mudah untuk dideteksi daripada mencoba melacak mengapa perangkat lunak secara misterius mengkonsumsi jumlah memori eksplosif setelah menjalankannya selama setengah jam sementara semua dari kami tes unit dan integrasi lulus tanpa keluhan (bahkan dari Valgrind, karena memori dibebaskan oleh GC saat dimatikan). Namun itu bukan slam pada GC di pihak saya atau upaya untuk mengatakan bahwa itu tidak berguna atau semacamnya, tapi itu belum ada peluru perak, bahkan tidak dekat, dalam tim saya bekerja dengan perangkat lunak bocor (untuk sebaliknya saya memiliki pengalaman yang berlawanan dengan satu basis kode yang menggunakan GC menjadi yang paling lemah yang pernah saya temui). Agar adil, banyak anggota di tim itu bahkan tidak tahu apa itu referensi yang lemah,
Kepemilikan dan Psikologi Bersama
Masalah yang saya temukan dengan pengumpulan sampah yang membuatnya sangat rentan terhadap "kebocoran memori" (dan saya akan bersikeras menyebutnya sebagai 'kebocoran ruang' berperilaku dengan cara yang sama persis dari perspektif pengguna-akhir) di tangan mereka yang tidak menggunakannya dengan hati-hati berhubungan dengan "kecenderungan manusia" sampai tingkat tertentu dalam pengalaman saya. Masalah dengan tim itu dan basis kode paling sedikit yang pernah saya temui adalah bahwa mereka tampaknya berada di bawah kesan bahwa GC akan memungkinkan mereka untuk berhenti memikirkan siapa yang memiliki sumber daya.
Dalam kasus kami, kami memiliki begitu banyak objek yang saling rujukan. Model akan merujuk bahan bersama dengan perpustakaan bahan dan sistem shader. Bahan akan referensi tekstur bersama dengan pustaka tekstur dan shader tertentu. Kamera akan menyimpan referensi ke semua jenis entitas adegan yang harus dikecualikan dari rendering. Daftar itu kelihatannya berlanjut tanpa batas. Itu membuat hampir semua sumber daya yang lumayan dalam sistem yang dimiliki dan diperpanjang seumur hidup di 10+ tempat lain di negara aplikasi sekaligus, dan itu sangat, sangat rentan terhadap kesalahan manusia dari jenis yang akan diterjemahkan menjadi kebocoran (dan tidak yang minor, saya berbicara gigabyte dalam hitungan menit dengan masalah kegunaan yang serius). Secara konseptual semua sumber daya ini tidak perlu dibagi dalam kepemilikan, mereka semua secara konseptual memiliki satu pemilik,
Jika kita berhenti berpikir tentang siapa yang memiliki memori apa, dan dengan senang hati hanya menyimpan referensi seumur hidup untuk objek di seluruh tempat tanpa memikirkan hal ini, maka perangkat lunak tidak akan crash sebagai akibat dari pointer yang menggantung tetapi hampir pasti, di bawah suatu pola pikir yang ceroboh, mulailah membocorkan ingatan seperti orang gila dengan cara yang sangat sulit dilacak dan akan lolos dari ujian.
Jika ada satu manfaat praktis untuk penunjuk menggantung di domain saya, itu menyebabkan gangguan dan crash yang sangat buruk. Dan itu cenderung setidaknya memberikan pengembang insentif, jika mereka ingin mengirimkan sesuatu yang dapat diandalkan, untuk mulai berpikir tentang manajemen sumber daya dan melakukan hal-hal yang diperlukan yang diperlukan untuk menghapus semua referensi tambahan / pointer ke objek yang tidak lagi dibutuhkan secara konseptual.
Manajemen Sumber Daya Aplikasi
Manajemen sumber daya yang tepat adalah nama permainan jika kita berbicara tentang menghindari kebocoran dalam aplikasi yang berumur panjang dengan keadaan terus-menerus disimpan di mana kebocoran akan menimbulkan frame rate yang serius dan masalah kegunaan. Dan mengelola sumber daya dengan benar di sini tidak kalah sulit dengan atau tanpa GC. Karya ini tidak kurang manual baik cara untuk menghapus referensi yang sesuai untuk objek tidak lagi diperlukan apakah itu pointer atau referensi yang memperpanjang seumur hidup.
Itulah tantangan di domain saya, tidak lupa dengan delete
apa yang kami new
(kecuali kami berbicara jam amatir dengan pengujian, praktik, dan alat jelek). Dan itu membutuhkan pemikiran dan kepedulian apakah kita menggunakan GC atau tidak.
Multithreading
Satu masalah lain yang saya temukan sangat berguna dengan GC, jika bisa digunakan dengan sangat hati-hati dalam domain saya, adalah menyederhanakan manajemen sumber daya dalam konteks multithreading. Jika kami berhati-hati untuk tidak menyimpan referensi perpanjangan sumber daya seumur hidup di lebih dari satu tempat dalam status aplikasi, maka sifat referensi GC yang diperluas seumur hidup bisa sangat berguna sebagai cara untuk utas untuk sementara memperluas sumber daya yang sedang diakses untuk memperluas masa pakainya hanya untuk durasi yang singkat seperti yang diperlukan agar utas selesai memprosesnya.
Saya pikir sangat hati-hati menggunakan GC dengan cara ini dapat menghasilkan sangat, perangkat lunak yang tidak bocor, sementara secara bersamaan menyederhanakan multithreading.
Ada beberapa cara untuk mengatasi hal ini meskipun tidak ada GC. Dalam kasus saya, kami menyatukan representasi entitas adegan perangkat lunak, dengan utas yang sementara menyebabkan sumber daya adegan diperpanjang untuk jangka waktu singkat dengan cara yang agak umum sebelum fase pembersihan. Ini mungkin sedikit berbau seperti GC tetapi perbedaannya adalah bahwa tidak ada "kepemilikan bersama" yang terlibat, hanya desain pemrosesan adegan yang seragam dalam utas yang menunda penghancuran sumber daya tersebut. Masih akan jauh lebih mudah untuk hanya mengandalkan GC di sini jika itu dapat digunakan dengan sangat hati-hati dengan pengembang yang teliti, hati-hati untuk menggunakan referensi yang lemah di area persisten yang relevan, untuk kasus multithreading tersebut.
C ++
Akhirnya:
Dalam C ++ saya harus memanggil delete untuk membuang objek yang dibuat pada akhir siklus hidup itu.
Di Modern C ++, ini umumnya bukan sesuatu yang harus Anda lakukan secara manual. Bahkan tidak banyak tentang lupa melakukannya. Ketika Anda melibatkan penanganan pengecualian ke dalam gambar, maka bahkan jika Anda menulis korespondensi di delete
bawah beberapa panggilan untuk new
, sesuatu bisa melempar di tengah dan tidak pernah mencapai delete
panggilan jika Anda tidak bergantung pada panggilan destruktor otomatis yang dimasukkan oleh kompiler untuk melakukan ini untuk kamu.
Dengan C ++ yang secara praktis Anda perlukan, kecuali jika Anda bekerja seperti konteks yang disematkan dengan pengecualian dan pustaka khusus yang sengaja diprogram untuk tidak membuang, hindari pembersihan sumber daya manual seperti itu (yang termasuk menghindari panggilan manual untuk membuka kunci di luar di luar dokumen. , misalnya, dan bukan hanya alokasi memori). Penanganan pengecualian cukup banyak menuntutnya, sehingga semua pembersihan sumber daya harus otomatis melalui destruktor untuk sebagian besar.