Saya tidak menanyakan pertanyaan ini karena manfaat pengumpulan sampah pertama-tama. Alasan utama saya untuk menanyakan hal ini adalah bahwa saya tahu bahwa Bjarne Stroustrup mengatakan bahwa C ++ akan memiliki pengumpul sampah di beberapa titik waktu.
Dengan itu, mengapa belum ditambahkan? Sudah ada beberapa pengumpul sampah untuk C ++. Apakah ini hanya salah satu dari hal-hal yang "lebih mudah diucapkan daripada dilakukan"? Atau adakah alasan lain mengapa itu belum ditambahkan (dan tidak akan ditambahkan dalam C ++ 11)?
Tautan silang:
Hanya untuk memperjelas, saya mengerti alasan mengapa C ++ tidak memiliki pengumpul sampah saat pertama kali dibuat. Saya ingin tahu mengapa kolektor tidak dapat ditambahkan.
c++
garbage-collection
c++11
Jason Baker
sumber
sumber
Jawaban:
Pengumpulan sampah secara implisit bisa saja ditambahkan, tetapi itu tidak berhasil. Mungkin karena tidak hanya komplikasi implementasi, tetapi juga karena orang tidak dapat mencapai konsensus umum dengan cukup cepat.
Kutipan dari Bjarne Stroustrup sendiri:
Ada diskusi yang bagus tentang topik ini di sini .
Gambaran umum:
C ++ sangat kuat dan memungkinkan Anda untuk melakukan hampir semua hal. Untuk alasan ini, itu tidak secara otomatis mendorong banyak hal ke Anda yang mungkin mempengaruhi kinerja. Pengumpulan sampah dapat dengan mudah diimplementasikan dengan pointer cerdas (objek yang membungkus pointer dengan jumlah referensi, yang otomatis menghapus sendiri ketika jumlah referensi mencapai 0).
C ++ dibangun dengan mempertimbangkan pesaing yang tidak memiliki pengumpulan sampah. Efisiensi adalah perhatian utama bahwa C ++ harus menangkis kritik dibandingkan C dan lainnya.
Ada 2 jenis pengumpulan sampah ...
Pengumpulan sampah eksplisit:
C ++ 0x akan memiliki pengumpulan sampah melalui pointer yang dibuat dengan shared_ptr
Jika Anda menginginkannya, Anda dapat menggunakannya, jika Anda tidak menginginkannya, Anda tidak dipaksa untuk menggunakannya.
Saat ini Anda dapat menggunakan boost: shared_ptr juga jika Anda tidak ingin menunggu C ++ 0x.
Pengumpulan sampah implisit:
Itu tidak memiliki pengumpulan sampah transparan. Ini akan menjadi titik fokus untuk spesifikasi C ++ di masa depan.
Mengapa Tr1 tidak memiliki pengumpulan sampah implisit?
Ada banyak hal yang seharusnya dimiliki oleh tr1 dari C ++ 0x, Bjarne Stroustrup dalam wawancara sebelumnya menyatakan bahwa tr1 tidak memiliki sebanyak yang dia inginkan.
sumber
smart_ptr's
? Bagaimana Anda melakukan forking gaya Unix tingkat rendah, dengan pengumpul sampah di jalan? Hal-hal lain yang akan terpengaruh seperti threading. Python memiliki kunci juru bahasa globalnya sebagian besar karena pengumpulan sampahnya (lihat Cython). Jauhkan dari C / C ++, terima kasih.std::shared_ptr
) adalah referensi siklus, yang menyebabkan kebocoran memori. Karena itu Anda harus hati-hati menggunakanstd::weak_ptr
untuk memutus siklus, yang berantakan. Tandai dan sapu gaya GC tidak memiliki masalah ini. Tidak ada ketidakcocokan yang melekat antara threading / forking dan pengumpulan sampah. Java dan C # keduanya memiliki multithreading preemptive kinerja tinggi dan dan seorang pengumpul sampah. Ada masalah yang harus dilakukan dengan aplikasi waktu nyata dan pengumpul sampah, karena sebagian besar pengumpul sampah harus menghentikan dunia untuk menjalankannya.std::shared_ptr
) adalah referensi siklus" dan kinerja yang buruk yang ironis karena kinerja yang lebih baik biasanya merupakan pembenaran untuk menggunakan C ++ ... flyingfrogblog.blogspot.co.uk/2011/01/…Untuk menambah perdebatan di sini.
Ada masalah yang diketahui dengan pengumpulan sampah, dan memahaminya membantu memahami mengapa tidak ada dalam C ++.
1. Kinerja?
Keluhan pertama sering tentang kinerja, tetapi kebanyakan orang tidak benar-benar menyadari apa yang mereka bicarakan. Seperti yang diilustrasikan oleh
Martin Beckett
masalah, mungkin bukan kinerja semata, melainkan kemampuan memprediksi kinerja.Saat ini ada 2 keluarga GC yang digunakan secara luas:
Itu
Mark And Sweep
lebih cepat (lebih sedikit berdampak pada kinerja keseluruhan) tetapi menderita sindrom "beku dunia": yaitu ketika GC masuk, semuanya dihentikan sampai GC melakukan pembersihan. Jika Anda ingin membangun server yang menjawab dalam beberapa milidetik ... beberapa transaksi tidak akan memenuhi harapan Anda :)Masalahnya
Reference Counting
berbeda: penghitungan referensi menambah overhead, terutama di lingkungan Multi-Threading karena Anda perlu memiliki jumlah atom. Selain itu ada masalah siklus referensi sehingga Anda memerlukan algoritma yang cerdas untuk mendeteksi siklus tersebut dan menghilangkannya (umumnya diterapkan oleh "beku dunia" juga, meskipun lebih jarang). Secara umum, seperti hari ini, jenis ini (meskipun biasanya lebih responsif atau lebih tepatnya, pembekuan lebih jarang) lebih lambat daripadaMark And Sweep
.Saya telah melihat sebuah makalah oleh para pelaksana Eiffel yang mencoba mengimplementasikan seorang
Reference Counting
Pengumpul Sampah yang akan memiliki kinerja global yang serupaMark And Sweep
tanpa aspek "Freeze The World". Untuk itu diperlukan utas terpisah untuk GC (tipikal). Algoritma itu agak menakutkan (pada akhirnya) tetapi makalah ini membuat pekerjaan yang baik untuk memperkenalkan konsep satu per satu dan menunjukkan evolusi algoritma dari versi "sederhana" ke yang penuh. Dianjurkan membaca jika hanya saya yang bisa mengembalikan file PDF ...2. Akuisisi Sumber Daya Adalah Inisialisasi (RAII)
Ini adalah idiom umum di
C++
mana Anda akan membungkus kepemilikan sumber daya dalam suatu objek untuk memastikan bahwa mereka dilepaskan dengan benar. Ini sebagian besar digunakan untuk memori karena kami tidak memiliki pengumpulan sampah, tetapi juga berguna untuk banyak situasi lain:Idenya adalah untuk mengontrol masa pakai objek dengan benar:
Masalah GC adalah bahwa jika membantu dengan yang pertama dan akhirnya menjamin bahwa nanti ... "akhir" ini mungkin tidak cukup. Jika Anda melepaskan kunci, Anda benar-benar ingin itu dilepaskan sekarang, sehingga tidak memblokir panggilan lebih lanjut!
Bahasa dengan GC memiliki dua bidang kerja:
using
membangun ... tapi itu eksplisit (lemah) RAII sementara di C ++ RAII tersirat sehingga pengguna TIDAK BISA tanpa sengaja membuat kesalahan (dengan menghilangkanusing
kata kunci)3. Pointer Cerdas
Pointer pintar sering muncul sebagai peluru perak untuk menangani memori
C++
. Sering kali saya mendengar: kita tidak membutuhkan GC, karena kita memiliki petunjuk pintar.Seseorang tidak bisa salah lagi.
Pointer pintar memang membantu:
auto_ptr
danunique_ptr
menggunakan konsep RAII, memang sangat berguna. Mereka sangat sederhana sehingga Anda dapat menulisnya sendiri dengan mudah.Namun, ketika seseorang perlu berbagi kepemilikan, hal itu menjadi lebih sulit: Anda mungkin berbagi di antara banyak utas dan ada beberapa masalah kecil dalam penanganan penghitungan. Karena itu, seseorang secara alami menuju
shared_ptr
.Ini bagus, itu yang mendorong untuk semua, tapi itu bukan peluru perak. Faktanya, masalah utamanya
shared_ptr
adalah bahwa ia mengemulasi GC yang diimplementasikan olehReference Counting
tetapi Anda perlu mengimplementasikan deteksi siklus sendiri ... UrgTentu ada ini
weak_ptr
, tapi sayangnya saya sudah melihat kebocoran memori meskipun menggunakanshared_ptr
karena siklus itu ... dan ketika Anda berada di lingkungan Multi Threaded, sangat sulit untuk dideteksi!4. Apa solusinya?
Tidak ada peluru perak, tetapi seperti biasa, itu pasti layak. Dengan tidak adanya GC seseorang harus jelas tentang kepemilikan:
weak_ptr
Jadi memang, akan bagus untuk memiliki GC ... namun itu bukan masalah sepele. Dan sementara itu, kita hanya perlu menyingsingkan lengan baju kita.
sumber
Tipe apa? apakah itu harus dioptimalkan untuk pengendali mesin cuci tertanam, ponsel, workstation atau superkomputer?
Haruskah memprioritaskan respons gui atau memuat server?
haruskah itu menggunakan banyak memori atau banyak CPU?
C / c ++ digunakan dalam banyak situasi yang berbeda. Saya menduga sesuatu seperti meningkatkan pointer pintar akan cukup untuk sebagian besar pengguna
Sunting - Pengumpul sampah otomatis tidak terlalu masalah kinerja (Anda selalu dapat membeli lebih banyak server) ini adalah masalah kinerja yang dapat diprediksi.
Tidak tahu kapan GC akan memulai adalah seperti mempekerjakan pilot maskapai narkolepsi, sebagian besar waktu mereka hebat - tetapi ketika Anda benar-benar membutuhkan respons!
sumber
Salah satu alasan terbesar bahwa C ++ tidak memiliki built in pengumpulan sampah adalah bahwa mendapatkan pengumpulan sampah untuk bermain bagus dengan destruktor benar-benar sangat sulit. Sejauh yang saya tahu, belum ada yang benar-benar tahu bagaimana menyelesaikannya sepenuhnya. Ada banyak masalah yang harus dihadapi:
Ini hanya beberapa masalah yang dihadapi.
sumber
Dispose
objek mungkin membuatnya tidak dapat digunakan, tetapi referensi yang menunjuk ke objek saat masih hidup akan terus melakukannya setelah mati. Sebaliknya, dalam sistem non-GC, objek dapat dihapus ketika referensi ada, dan jarang ada batasan untuk kekacauan yang dapat melampiaskan jika salah satu referensi tersebut digunakan.Padahal ini sudah tua pertanyaan , masih ada satu masalah yang saya tidak melihat ada yang ditangani sama sekali: pengumpulan sampah hampir mustahil untuk ditentukan.
Secara khusus, standar C ++ cukup hati-hati untuk menentukan bahasa dalam hal perilaku yang dapat diamati secara eksternal, daripada bagaimana implementasi mencapai perilaku itu. Dalam kasus pengumpulan sampah, namun, ada yang hampir tidak ada eksternal perilaku yang dapat diamati.
The ide umum dari pengumpulan sampah adalah bahwa hal itu harus membuat upaya yang wajar di meyakinkan bahwa alokasi memori akan berhasil. Sayangnya, pada dasarnya tidak mungkin untuk menjamin bahwa alokasi memori apa pun akan berhasil, bahkan jika Anda memiliki pengumpul sampah yang beroperasi. Ini benar sampai batas tertentu dalam kasus apa pun, tetapi khususnya dalam kasus C ++, karena (mungkin) tidak mungkin menggunakan kolektor penyalinan (atau yang serupa) yang memindahkan objek dalam memori selama siklus pengumpulan.
Jika Anda tidak dapat memindahkan objek, Anda tidak dapat membuat satu ruang memori yang berdekatan untuk melakukan alokasi Anda - dan itu berarti tumpukan Anda (atau toko gratis, atau apa pun yang Anda suka menyebutnya) dapat, dan mungkin akan , menjadi terfragmentasi dari waktu ke waktu. Ini, pada gilirannya, dapat mencegah alokasi tidak berhasil, bahkan ketika ada lebih banyak memori yang bebas dari jumlah yang diminta.
Meskipun dimungkinkan untuk mendapatkan beberapa jaminan yang mengatakan (pada dasarnya) bahwa jika Anda mengulangi pola alokasi yang sama berulang kali, dan itu berhasil pertama kali, itu akan terus berhasil pada iterasi berikutnya, asalkan memori yang dialokasikan menjadi tidak dapat diakses di antara iterasi. Jaminan yang sangat lemah itu pada dasarnya tidak berguna, tapi saya tidak bisa melihat harapan yang masuk akal untuk memperkuatnya.
Meski begitu, itu lebih kuat dari apa yang telah diusulkan untuk C ++. The Proposal sebelumnya [peringatan: PDF] (yang punya turun) tidak apa-apa jaminan sama sekali. Dalam 28 halaman proposal, apa yang Anda dapatkan dari perilaku yang dapat diamati secara eksternal adalah sebuah catatan tunggal (non-normatif) yang mengatakan:
Setidaknya bagi saya, ini menimbulkan pertanyaan serius tentang pengembalian investasi. Kita akan memecahkan kode yang ada (tidak ada yang tahu pasti berapa banyak, tapi pasti sedikit), menempatkan persyaratan baru pada implementasi dan pembatasan baru pada kode, dan apa yang kita dapatkan sebagai balasannya sangat mungkin tidak ada sama sekali?
Bahkan yang terbaik, yang kami dapatkan adalah program yang, berdasarkan pengujian dengan Java , mungkin akan membutuhkan sekitar enam kali lebih banyak memori untuk berjalan pada kecepatan yang sama seperti yang mereka lakukan sekarang. Lebih buruk lagi, pengumpulan sampah adalah bagian dari Jawa sejak awal - C ++ menempatkan lebih banyak pembatasan pada pengumpul sampah sehingga hampir pasti akan memiliki rasio biaya / manfaat yang lebih buruk (bahkan jika kita melampaui apa yang dijamin proposal dan berasumsi akan ada beberapa manfaat).
Saya meringkas situasi ini secara matematis: ini situasi yang kompleks. Sebagaimana diketahui oleh ahli matematika, bilangan kompleks memiliki dua bagian: nyata dan imajiner. Tampak bagi saya bahwa apa yang kita miliki di sini adalah biaya yang nyata, tetapi manfaat yang (setidaknya sebagian besar) imajiner.
sumber
free
Anda (di mana saya bermaksudfree
analagous ke bahasa C). Tetapi Java tidak pernah menjamin untuk memanggil finalizer atau semacamnya. Faktanya, C ++ tidak lebih dari Java untuk dijalankan di sekitar komit database, pembilasan menangani file, dan sebagainya. Java mengklaim memiliki "GC", tetapi pengembang Java harus teliti meneleponclose()
sepanjang waktu dan mereka harus sangat menyadari manajemen sumber daya, berhati-hati untuk tidak meneleponclose()
terlalu cepat atau terlambat. C ++ membebaskan kita dari itu. ... (lanjutan)try (Whatever w=...) {...}
selesaikan (dan Anda mendapat peringatan saat Anda lupa). Yang tersisa juga bermasalah dengan RAII. Memanggilclose()
"sepanjang waktu" berarti mungkin sekali per puluhan ribu baris, jadi itu tidak terlalu buruk, sementara memori dialokasikan hampir di setiap baris Java.Sumber: http://www.stroustrup.com/bs_faq.html#garbage-collection
Adapun mengapa ia tidak memilikinya dibangun, Jika saya ingat benar itu diciptakan sebelum GC adalah hal , dan saya tidak percaya bahasa bisa memiliki GC karena beberapa alasan (IE Backwards kompatibel dengan C)
Semoga ini membantu.
sumber
Stroustrup membuat beberapa komentar bagus tentang ini di konferensi Going Native 2013.
Lewati saja sekitar 25m50s dalam video ini . (Saya sarankan menonton seluruh video sebenarnya, tetapi ini melompati hal-hal tentang pengumpulan sampah.)
Ketika Anda memiliki bahasa yang benar-benar hebat yang membuatnya mudah (dan aman, dapat diprediksi, dan mudah dibaca, dan mudah diajari) untuk berurusan dengan objek dan nilai secara langsung, menghindari (eksplisit) penggunaan tumpukan, maka Anda bahkan tidak ingin pengumpulan sampah.
Dengan C ++ modern, dan barang-barang yang kami miliki di C ++ 11, pengumpulan sampah tidak lagi diinginkan kecuali dalam keadaan terbatas. Bahkan, bahkan jika pengumpul sampah yang baik dibangun ke dalam salah satu kompiler C ++ utama, saya pikir itu tidak akan sering digunakan. Akan lebih mudah , tidak sulit, untuk menghindari GC.
Dia menunjukkan contoh ini:
Ini tidak aman di C ++. Tetapi juga tidak aman di Jawa! Dalam C ++, jika fungsi kembali lebih awal,
delete
tidak akan pernah dipanggil. Tetapi jika Anda memiliki pengumpulan sampah penuh, seperti di Jawa, Anda hanya mendapatkan saran bahwa objek tersebut akan dihancurkan "di beberapa titik di masa depan" ( Pembaruan: bahkan lebih buruk dari ini. Java tidakberjanji untuk memanggil finalizer - itu mungkin tidak pernah disebut). Ini tidak cukup baik jika Gadget memegang pegangan file terbuka, atau koneksi ke database, atau data yang telah Anda buffer untuk menulis ke database di kemudian hari. Kami ingin Gadget dimusnahkan segera setelah selesai, untuk membebaskan sumber daya ini sesegera mungkin. Anda tidak ingin server database Anda berjuang dengan ribuan koneksi database yang tidak lagi diperlukan - ia tidak tahu bahwa program Anda selesai berfungsi.Jadi apa solusinya? Ada beberapa pendekatan. Pendekatan yang jelas, yang akan Anda gunakan untuk sebagian besar objek Anda adalah:
Ini membutuhkan lebih sedikit karakter untuk diketik. Tidak ada
new
yang menghalangi. Anda tidak perlu mengetikGadget
dua kali. Objek dihancurkan di akhir fungsi. Jika ini yang Anda inginkan, ini sangat intuitif.Gadget
Berperilaku sama denganint
ataudouble
. Mudah ditebak, mudah dibaca, mudah diajarkan. Semuanya adalah 'nilai'. Terkadang nilai yang besar, tetapi nilai lebih mudah untuk diajarkan karena Anda tidak memiliki 'tindakan di kejauhan' yang Anda dapatkan dengan pointer (atau referensi).Sebagian besar objek yang Anda buat hanya untuk digunakan dalam fungsi yang membuatnya, dan mungkin diteruskan sebagai input ke fungsi anak. Pemrogram tidak harus berpikir tentang 'manajemen memori' ketika mengembalikan objek, atau berbagi objek di seluruh bagian perangkat lunak yang terpisah.
Lingkup dan umur adalah penting. Sebagian besar waktu, lebih mudah jika seumur hidup sama dengan ruang lingkup. Lebih mudah dimengerti dan lebih mudah untuk diajar. Ketika Anda menginginkan masa hidup yang berbeda, harus jelas membaca kode yang Anda lakukan ini, dengan menggunakan
shared_ptr
misalnya. (Atau mengembalikan objek (besar) dengan nilai, meningkatkan gerakan-semantik atauunique_ptr
.Ini mungkin tampak seperti masalah efisiensi. Bagaimana jika saya ingin mengembalikan Gadget
foo()
? Semantik bergerak C ++ 11 membuatnya lebih mudah untuk mengembalikan objek besar. Cukup tulisGadget foo() { ... }
dan itu hanya akan bekerja, dan bekerja dengan cepat. Anda tidak perlu mengacaukan&&
diri sendiri, kembalikan saja dengan nilai dan bahasa akan sering dapat melakukan optimasi yang diperlukan. (Bahkan sebelum C ++ 03, kompiler melakukan pekerjaan yang sangat baik untuk menghindari penyalinan yang tidak perlu.)Seperti yang dikatakan Stroustrup di bagian lain dalam video (parafrase): "Hanya seorang ilmuwan komputer yang bersikeras menyalin objek, dan kemudian menghancurkan yang asli. (Hadirin tertawa). Mengapa tidak hanya memindahkan objek secara langsung ke lokasi baru? Inilah yang dilakukan manusia (bukan ilmuwan komputer) harapkan. "
Ketika Anda dapat menjamin hanya satu salinan dari suatu objek yang dibutuhkan, akan lebih mudah untuk memahami umur dari objek tersebut. Anda dapat memilih kebijakan seumur hidup yang Anda inginkan, dan pengumpulan sampah ada jika Anda mau. Tetapi ketika Anda memahami manfaat dari pendekatan lain, Anda akan menemukan bahwa pengumpulan sampah ada di bagian bawah daftar preferensi Anda.
Jika itu tidak berhasil untuk Anda, Anda dapat menggunakan
unique_ptr
, atau gagalshared_ptr
,. C ++ 11 yang ditulis dengan baik lebih pendek, lebih mudah dibaca, dan lebih mudah diajarkan daripada banyak bahasa lain dalam hal manajemen memori.sumber
Gadget
tidak meminta hal lain untuk melakukan apa pun atas namanya, kode asli akan sepenuhnya aman di Jawa jika pernyataan yang tidak bermakna (ke Jawa)delete
dihapus.shared_ptr<T>
secara khusus ketikaT
'membosankan'. Itu bisa memutuskan untuk tidak benar-benar mengelola penghitung referensi untuk tipe itu, dan sebagai gantinya menggunakan GC. Ini akan memungkinkan GC untuk digunakan tanpa pengembang perlu memperhatikan. Ashared_ptr
hanya dapat dilihat sebagai pointer GC, untuk yang sesuaiT
. Tetapi ada batasan dalam hal ini, dan itu akan membuat banyak program lebih lambat.string1=string2;
akan dieksekusi dengan sangat cepat terlepas dari panjang string (itu benar-benar tidak lebih dari sebuah register load dan register store), dan tidak memerlukan penguncian untuk memastikan bahwa jika pernyataan di atas dieksekusi ketikastring2
sedang sedang ditulis,string1
akan memiliki nilai lama atau nilai baru, tanpa Perilaku Tidak Terdefinisi).shared_ptr<String>
membutuhkan banyak sinkronisasi di belakang layar, dan penugasan aString
dapat berperilaku aneh jika suatu variabel dibaca dan ditulis secara bersamaan. Kasus di mana seseorang ingin menulis dan membaca secaraString
bersamaan tidak terlalu umum, tetapi dapat muncul jika misalnya beberapa kode ingin membuat laporan status yang sedang berlangsung tersedia untuk utas lainnya. Dalam. NET dan Java, hal-hal seperti itu hanya "berfungsi".Karena C ++ modern tidak memerlukan pengumpulan sampah.
Jawaban FAQ Bjarne Stroustrup tentang masalah ini mengatakan :
Situasi, untuk kode yang ditulis hari ini (C ++ 17 dan mengikuti Pedoman Inti resmi ) adalah sebagai berikut:
"Oh ya? Tapi bagaimana dengan ...
... jika saya hanya menulis kode seperti dulu menulis C ++ di masa lalu? "
Memang, Anda bisa mengabaikan semua panduan dan menulis kode aplikasi yang bocor - dan itu akan dikompilasi dan dijalankan (dan bocor), sama seperti biasanya.
Tapi ini bukan situasi "jangan lakukan itu", di mana pengembang diharapkan berbudi luhur dan banyak melakukan kontrol diri; itu tidak mudah untuk menulis kode yang tidak sesuai, juga tidak lebih cepat untuk menulis, juga tidak berkinerja lebih baik. Secara bertahap juga akan menjadi lebih sulit untuk menulis, karena Anda akan menghadapi "ketidakcocokan impedansi" yang meningkat dengan apa yang disediakan dan diharapkan oleh kode yang sesuai.
... jika saya
reintrepret_cast
? Atau apakah aritmatika pointer kompleks? Atau peretasan semacam itu? "Memang, jika Anda memusatkan perhatian pada hal itu, Anda dapat menulis kode yang mengacaukan segalanya meskipun bermain baik dengan pedoman. Tapi:
... pengembangan perpustakaan? "
Jika Anda seorang pengembang perpustakaan C ++ maka Anda menulis kode tidak aman yang melibatkan pointer mentah, dan Anda diharuskan untuk membuat kode dengan hati-hati dan bertanggung jawab - tetapi ini adalah potongan kode mandiri yang ditulis oleh para ahli (dan yang lebih penting, ditinjau oleh para ahli).
Jadi, seperti yang dikatakan Bjarne: Sebenarnya tidak ada motivasi untuk mengumpulkan sampah secara umum, seperti Anda semua tetapi pastikan untuk tidak menghasilkan sampah. GC menjadi tidak masalah dengan C ++.
Itu tidak berarti bahwa GC bukan masalah yang menarik untuk aplikasi spesifik tertentu, ketika Anda ingin menggunakan strategi alokasi khusus dan de-alokasi. Bagi Anda yang ingin alokasi khusus dan de-alokasi, bukan GC tingkat bahasa.
sumber
Gagasan di balik C ++ adalah bahwa Anda tidak akan membayar dampak kinerja apa pun untuk fitur yang tidak Anda gunakan. Jadi menambahkan pengumpulan sampah akan berarti memiliki beberapa program berjalan langsung pada perangkat keras seperti yang dilakukan C dan beberapa dalam semacam mesin virtual runtime.
Tidak ada yang mencegah Anda menggunakan beberapa bentuk pointer cerdas yang terikat pada mekanisme pengumpulan sampah pihak ketiga. Sepertinya saya ingat Microsoft melakukan sesuatu seperti itu dengan COM dan itu tidak berjalan dengan baik.
sumber
Untuk menjawab sebagian besar pertanyaan "mengapa" tentang C ++, baca Desain dan Evolusi C ++
sumber
Salah satu prinsip dasar di balik bahasa C asli adalah bahwa memori terdiri dari urutan byte, dan kode hanya perlu peduli tentang apa arti byte tersebut pada saat yang tepat ketika mereka sedang digunakan. Modern C memungkinkan kompiler untuk memberlakukan batasan tambahan, tetapi C termasuk - dan C ++ tetap - kemampuan untuk menguraikan pointer menjadi urutan byte, merakit urutan byte yang berisi nilai yang sama ke dalam pointer, dan kemudian menggunakan pointer itu untuk mengakses objek sebelumnya.
Sementara kemampuan itu dapat berguna - atau bahkan sangat diperlukan - dalam beberapa jenis aplikasi, bahasa yang mencakup kemampuan itu akan sangat terbatas dalam kemampuannya untuk mendukung segala jenis pengumpulan sampah yang berguna dan andal. Jika kompiler tidak mengetahui semua yang telah dilakukan dengan bit yang membentuk sebuah pointer, ia tidak akan memiliki cara untuk mengetahui apakah informasi yang cukup untuk merekonstruksi pointer mungkin ada di suatu tempat di alam semesta. Karena mungkin saja informasi itu disimpan dengan cara yang tidak akan dapat diakses oleh komputer walaupun ia tahu tentang mereka (mis. Byte yang membentuk pointer mungkin telah ditampilkan di layar cukup lama bagi seseorang untuk menulis mereka di selembar kertas), mungkin secara harfiah tidak mungkin bagi komputer untuk mengetahui apakah pointer mungkin dapat digunakan di masa depan.
Sebuah kekhasan yang menarik dari banyak kerangka kerja pengumpulan sampah adalah bahwa referensi objek tidak ditentukan oleh pola bit yang terkandung di dalamnya, tetapi oleh hubungan antara bit yang disimpan dalam referensi objek dan informasi lainnya yang disimpan di tempat lain. Dalam C dan C ++, jika pola bit yang disimpan dalam pointer mengidentifikasi suatu objek, pola bit itu akan mengidentifikasi objek itu sampai objek tersebut secara eksplisit dihancurkan. Dalam sistem GC tipikal, suatu objek dapat diwakili oleh pola bit 0x1234ABCD pada satu saat, tetapi siklus GC berikutnya mungkin mengganti semua referensi ke 0x1234ABCD dengan referensi ke 0x4321BABE, dimana objek tersebut akan diwakili oleh pola yang terakhir. Bahkan jika seseorang menampilkan pola bit yang terkait dengan referensi objek dan kemudian membacanya kembali dari keyboard,
sumber
Semua pembicaraan teknis adalah konsep yang terlalu rumit.
Jika Anda memasukkan GC ke dalam C ++ untuk semua memori secara otomatis, maka pertimbangkan sesuatu seperti browser web. Peramban web harus memuat dokumen web lengkap DAN menjalankan skrip web. Anda bisa menyimpan variabel skrip web di pohon dokumen. Dalam dokumen BESAR di peramban dengan banyak tab terbuka, itu berarti bahwa setiap kali GC harus melakukan pengumpulan penuh, ia juga harus memindai semua elemen dokumen.
Pada kebanyakan komputer, ini berarti bahwa HALAMAN HALAMAN akan terjadi. Jadi alasan utama, untuk menjawab pertanyaan itu adalah bahwa HAL-HAL HAL akan terjadi. Anda akan mengetahui ini ketika PC Anda mulai membuat banyak akses disk. Ini karena GC harus menyentuh banyak memori untuk membuktikan pointer yang tidak valid. Ketika Anda memiliki aplikasi yang bonafide menggunakan banyak memori, harus memindai semua objek setiap koleksi adalah malapetaka karena HAL-HAL YANG HAL. Kesalahan halaman adalah ketika memori virtual perlu dibaca kembali ke dalam RAM dari disk.
Jadi solusi yang tepat adalah dengan membagi aplikasi menjadi bagian-bagian yang membutuhkan GC dan bagian-bagian yang tidak. Dalam kasus contoh browser web di atas, jika pohon dokumen dialokasikan dengan malloc, tetapi javascript berjalan dengan GC, maka setiap kali tendangan GC di dalamnya hanya memindai sebagian kecil dari memori dan semua elemen PAGED OUT dari memori untuk pohon dokumen tidak perlu mendapatkan paged kembali.
Untuk lebih memahami masalah ini, cari di memori virtual dan bagaimana itu diterapkan di komputer. Ini semua tentang fakta bahwa 2GB tersedia untuk program ketika tidak ada RAM yang terlalu banyak. Pada komputer modern dengan RAM 2GB untuk sistem 32BIt bukan masalah seperti itu asalkan hanya satu program yang berjalan.
Sebagai contoh tambahan, pertimbangkan koleksi lengkap yang harus melacak semua objek. Pertama, Anda harus memindai semua objek yang dapat dijangkau melalui root. Pindai kedua semua objek yang terlihat di langkah 1. Kemudian pindai destructor yang menunggu. Lalu pergi ke semua halaman lagi dan matikan semua objek yang tidak terlihat. Ini berarti bahwa banyak halaman mungkin akan diganti dan kembali beberapa kali.
Jadi jawaban saya singkatnya adalah bahwa jumlah PAGE FAULTS yang terjadi akibat menyentuh semua memori menyebabkan GC penuh untuk semua objek dalam suatu program menjadi tidak layak sehingga programmer harus melihat GC sebagai bantuan untuk hal-hal seperti skrip dan kerja basis data, tetapi melakukan hal-hal normal dengan manajemen memori manual.
Dan alasan lain yang sangat penting tentu saja adalah variabel global. Agar kolektor mengetahui bahwa penunjuk variabel global ada dalam GC, ia akan memerlukan kata kunci tertentu, dan dengan demikian kode C ++ yang ada tidak akan berfungsi.
sumber
JAWABAN SINGKAT: Kami tidak tahu bagaimana melakukan pengumpulan sampah secara efisien (dengan waktu dan ruang minor) dan benar sepanjang waktu (dalam semua kasus yang memungkinkan).
JAWABAN LAMA: Sama seperti C, C ++ adalah bahasa sistem; ini berarti digunakan ketika Anda menulis kode sistem, misalnya sistem operasi. Dengan kata lain, C ++ dirancang, sama seperti C, dengan kinerja terbaik sebagai target utama. Standar bahasa tidak akan menambahkan fitur apa pun yang dapat menghalangi tujuan kinerja.
Ini menjeda pertanyaan: Mengapa pengumpulan sampah menghalangi kinerja? Alasan utama adalah bahwa, ketika datang ke implementasi, kami [ilmuwan komputer] tidak tahu bagaimana melakukan pengumpulan sampah dengan overhead yang minimal, untuk semua kasus. Karenanya tidak mungkin bagi kompiler C dan sistem runtime untuk melakukan pengumpulan sampah secara efisien sepanjang waktu. Di sisi lain, seorang programmer C ++, harus tahu desain / implementasinya dan dia adalah orang terbaik untuk memutuskan cara terbaik melakukan pengumpulan sampah.
Terakhir, jika kontrol (perangkat keras, detail, dll.) Dan kinerja (waktu, ruang, daya, dll.) Bukan kendala utama, maka C ++ bukanlah alat tulis. Bahasa lain mungkin berfungsi lebih baik dan menawarkan manajemen runtime [tersembunyi] yang lebih banyak, dengan overhead yang diperlukan.
sumber
Ketika kami membandingkan C ++ dengan Java, kami melihat bahwa C ++ tidak dirancang dengan pengumpulan sampah implisit dalam pikiran, sementara Java.
Memiliki hal-hal seperti pointer sewenang-wenang di C-Style tidak hanya buruk untuk implementasi GC, tetapi juga akan menghancurkan kompatibilitas ke belakang untuk sejumlah besar C ++ - legacy-code.
Selain itu, C ++ adalah bahasa yang dimaksudkan untuk dijalankan sebagai executable mandiri alih-alih memiliki lingkungan run-time yang kompleks.
Semua dalam semua: Ya mungkin untuk menambahkan Koleksi Sampah ke C ++, tetapi demi kontinuitas lebih baik tidak melakukannya.
sumber
Terutama karena dua alasan:
C ++ sudah menawarkan manajemen memori manual, alokasi tumpukan, RAII, wadah, pointer otomatis, pointer pintar ... Itu sudah cukup. Pengumpul sampah adalah untuk pemrogram yang malas yang tidak ingin menghabiskan 5 menit untuk memikirkan siapa yang harus memiliki benda atau kapan sumber daya harus dibebaskan. Bukan itu yang kami lakukan di C ++.
sumber
Memaksakan pengumpulan sampah benar-benar merupakan pergeseran paradigma level rendah ke level tinggi.
Jika Anda melihat cara string ditangani dalam bahasa dengan pengumpulan sampah, Anda akan menemukan bahwa HANYA memungkinkan fungsi manipulasi string tingkat tinggi dan tidak memungkinkan akses biner ke string. Sederhananya, semua fungsi string pertama memeriksa pointer untuk melihat di mana string itu, bahkan jika Anda hanya menggambar byte. Jadi jika Anda melakukan loop yang memproses setiap byte dalam string dalam bahasa dengan pengumpulan sampah, ia harus menghitung lokasi basis ditambah offset untuk setiap iterasi, karena ia tidak bisa tahu kapan string telah dipindahkan. Maka Anda harus berpikir tentang tumpukan, tumpukan, utas, dll.
sumber