Mengapa C ++ tidak memiliki pengumpul sampah?

270

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.

Jason Baker
sumber
26
Ini adalah salah satu dari sepuluh mitos tentang C ++ yang selalu diangkat oleh para pembenci. Pengumpulan sampah bukan "bawaan", tetapi ada beberapa cara mudah untuk melakukannya C ++. Posting komentar karena orang lain sudah menjawab lebih baik daripada yang saya dapat di bawah ini :)
davr
5
Tapi itulah inti dari tidak built-in, Anda harus melakukannya sendiri. Realibilitas dari tinggi ke rendah: built-in, perpustakaan, buatan sendiri. Saya menggunakan C ++ sendiri, dan jelas bukan pembenci karena itu adalah bahasa terbaik di dunia. Tetapi manajemen memori yang dinamis itu menyebalkan.
QBziZ
4
@Davr - Saya bukan pembenci C ++ juga saya bahkan tidak mencoba untuk berdebat bahwa C ++ membutuhkan pemulung. Saya bertanya karena saya tahu bahwa Bjarne Stroustrup telah mengatakan bahwa itu AKAN ditambahkan dan hanya ingin tahu apa alasan untuk tidak menerapkannya.
Jason Baker
1
Artikel ini The Boehm Collector untuk C dan C ++ dari Dr. Dobbs menjelaskan tentang pengumpul sampah open source yang dapat digunakan dengan C dan C ++. Ini membahas beberapa masalah yang muncul dengan menggunakan pengumpul sampah dengan destruktor C ++ serta Perpustakaan Standar C.
Richard Chambers
1
@rogerdpack: Tapi itu tidak berguna sekarang (lihat jawaban saya ...) jadi implementasi yang tidak mungkin akan berinvestasi untuk memilikinya.
einpoklum

Jawaban:

160

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:

Saya berharap bahwa seorang pengumpul sampah yang secara opsional dapat diaktifkan akan menjadi bagian dari C ++ 0x, tetapi ada cukup masalah teknis yang harus saya lakukan hanya dengan spesifikasi terperinci tentang bagaimana pengumpul tersebut terintegrasi dengan bagian bahasa lainnya. , jika disediakan. Seperti halnya pada dasarnya semua fitur C ++ 0x, implementasi eksperimental ada.

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.

Brian R. Bondy
sumber
71
Saya akan menjadi pembenci jika C ++ memaksaku mengumpulkan sampah! Mengapa orang tidak bisa menggunakan 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.
unixman83
26
@ unixman83: Masalah utama dengan referensi pengumpulan sampah terhitung (yaitu std::shared_ptr) adalah referensi siklus, yang menyebabkan kebocoran memori. Karena itu Anda harus hati-hati menggunakan std::weak_ptruntuk 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.
Andrew Tomazos
9
"Masalah utama dengan referensi pengumpulan sampah yang dihitung (yaitu 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/…
Jon Harrop
11
"Bagaimana Anda melakukan forking gaya Unix tingkat rendah". Cara yang sama bahasa GC seperti OCaml telah melakukannya selama ~ 20 tahun atau lebih.
Jon Harrop
9
"Python memiliki kunci penerjemah globalnya sebagian besar karena pengumpulan sampahnya". Argumen Strawman. Java dan. NET keduanya memiliki GC tetapi juga tidak memiliki kunci global.
Jon Harrop
149

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 Beckettmasalah, mungkin bukan kinerja semata, melainkan kemampuan memprediksi kinerja.

Saat ini ada 2 keluarga GC yang digunakan secara luas:

  • Jenis-dan-Sapu baik
  • Jenis penghitungan referensi

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 Countingberbeda: 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 daripada Mark And Sweep.

Saya telah melihat sebuah makalah oleh para pelaksana Eiffel yang mencoba mengimplementasikan seorang Reference CountingPengumpul Sampah yang akan memiliki kinerja global yang serupa Mark And Sweeptanpa 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:

  • kunci (multi-utas, pegangan file, ...)
  • koneksi (ke database, server lain, ...)

Idenya adalah untuk mengontrol masa pakai objek dengan benar:

  • itu harus hidup selama Anda membutuhkannya
  • itu harus dibunuh ketika Anda selesai dengan itu

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:

  • jangan gunakan GC ketika alokasi tumpukan cukup: biasanya untuk masalah kinerja, tetapi dalam kasus kami ini sangat membantu karena ruang lingkup menentukan masa pakai
  • usingmembangun ... tapi itu eksplisit (lemah) RAII sementara di C ++ RAII tersirat sehingga pengguna TIDAK BISA tanpa sengaja membuat kesalahan (dengan menghilangkan usingkata 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_ptrdan unique_ptrmenggunakan 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_ptradalah bahwa ia mengemulasi GC yang diimplementasikan oleh Reference Countingtetapi Anda perlu mengimplementasikan deteksi siklus sendiri ... Urg

Tentu ada ini weak_ptr , tapi sayangnya saya sudah melihat kebocoran memori meskipun menggunakan shared_ptrkarena 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:

  • lebih suka memiliki pemilik tunggal pada satu waktu tertentu, jika memungkinkan
  • jika tidak, pastikan diagram kelas Anda tidak memiliki siklus yang berkaitan dengan kepemilikan dan hancurkan mereka dengan aplikasi halus weak_ptr

Jadi memang, akan bagus untuk memiliki GC ... namun itu bukan masalah sepele. Dan sementara itu, kita hanya perlu menyingsingkan lengan baju kita.

Matthieu M.
sumber
2
Saya berharap saya bisa menerima dua jawaban! Ini bagus sekali. Satu hal yang perlu diperhatikan, dalam hal kinerja, GC yang berjalan di utas terpisah sebenarnya cukup umum (digunakan di Jawa dan .Net). Memang, itu mungkin tidak dapat diterima dalam sistem embedded.
Jason Baker
14
Hanya dua jenis? Bagaimana kalau menyalin kolektor? Kolektor generasi? Pengumpul serentak yang beraneka ragam (termasuk treadmill real-time keras milik Baker)? Berbagai kolektor hybrid? Man, ketidaktahuan semata-mata dalam industri bidang ini kadang-kadang membuatku heran.
HANYA SAYA PENDAPAT benar
12
Apakah saya mengatakan hanya ada 2 jenis? Saya mengatakan bahwa ada 2 yang digunakan secara luas. Sejauh yang saya tahu Python, Java dan C # semua menggunakan algoritma Mark and Sweep sekarang (Java digunakan untuk memiliki algoritma penghitungan referensi). Lebih tepatnya, menurut saya daripada C # menggunakan Generational GC untuk siklus kecil, Tandai Dan Sapu untuk siklus utama dan Menyalin untuk melawan fragmentasi memori; meskipun saya berpendapat bahwa inti dari algoritma ini adalah Mark And Sweep. Apakah Anda tahu bahasa utama yang menggunakan teknologi lain? Saya selalu senang belajar.
Matthieu M.
3
Anda baru saja menyebutkan satu bahasa utama yang menggunakan tiga.
HANYA SAYA PENDAPAT benar
3
Perbedaan utama adalah Generational dan Incremental GC tidak perlu menghentikan dunia untuk bekerja, dan Anda dapat membuatnya bekerja pada sistem single-threaded tanpa terlalu banyak biaya overhead dengan sesekali melakukan iterasi dari traversal pohon ketika mengakses pointer GC (faktornya dapat ditentukan oleh jumlah node baru, bersama dengan prediksi dasar dari kebutuhan untuk mengumpulkan). Anda dapat mengambil GC lebih jauh dengan memasukkan data tentang di mana dalam kode pembuatan / modifikasi node terjadi, yang dapat memungkinkan Anda untuk meningkatkan prediksi Anda, dan Anda mendapatkan Escape Analysis secara gratis dengannya.
Keldon Alleyne
56

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!

Martin Beckett
sumber
6
Saya benar-benar mengerti maksud Anda, tetapi saya merasa terdorong untuk bertanya: bukankah Java digunakan dalam banyak aplikasi?
Jason Baker
35
Tidak. Java tidak cocok untuk aplikasi berkinerja tinggi, karena alasan sederhana bahwa ia tidak memiliki jaminan kinerja yang sama dengan C ++. Jadi, Anda akan menemukannya di ponsel, tetapi Anda tidak akan menemukannya di sakelar sel atau superkomputer.
Zathrus
11
Anda selalu dapat membeli lebih banyak server, tetapi Anda tidak selalu dapat membeli lebih banyak CPU untuk ponsel yang sudah ada di saku pelanggan!
Crashworks
8
Java telah melakukan banyak peningkatan kinerja dalam efisiensi CPU. Masalah yang benar-benar sulit dipecahkan adalah penggunaan memori, Java secara inheren kurang efisien dari C ++. Dan ketidakefisienan itu disebabkan oleh fakta bahwa itu adalah sampah yang dikumpulkan. Pengumpulan sampah tidak bisa cepat dan efisien memori, fakta yang menjadi jelas jika Anda melihat seberapa cepat algoritma GC bekerja.
Nate CK
2
@Zathrus java dapat menang pada throughput b / c dari mengoptimalkan jit, meskipun bukan latensi (boo real-time), dan tentu saja bukan jejak memori.
gtrak
34

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:

  • seumur hidup deterministik objek (penghitungan referensi memberi Anda ini, tetapi GC tidak. Meskipun mungkin bukan masalah besar).
  • apa yang terjadi jika destruktor melempar ketika objek sedang dikumpulkan sampah? Sebagian besar bahasa mengabaikan pengecualian ini, karena tidak ada blok tangkap untuk dapat memindahkannya, tetapi ini mungkin bukan solusi yang dapat diterima untuk C ++.
  • Bagaimana cara mengaktifkan / menonaktifkannya? Tentu itu mungkin akan menjadi keputusan waktu kompilasi tetapi kode yang ditulis untuk GC vs kode yang ditulis untuk TIDAK GC akan sangat berbeda dan mungkin tidak kompatibel. Bagaimana Anda mendamaikan ini?

Ini hanya beberapa masalah yang dihadapi.

Greg Rogers
sumber
17
GC dan destruktor adalah masalah yang diselesaikan, dengan langkah yang bagus dari Bjarne. Destructor tidak berjalan selama GC, karena itu bukan titik GC. GC dalam C ++ ada untuk membuat gagasan tentang memori tak terbatas , bukan sumber daya tak terbatas lainnya.
MSalters
2
Jika destruktor tidak berjalan yang sepenuhnya mengubah semantik bahasa. Saya kira setidaknya Anda memerlukan kata kunci baru "gcnew" atau sesuatu agar Anda secara eksplisit mengizinkan objek ini menjadi GC'ed (dan karenanya Anda tidak boleh menggunakannya untuk membungkus sumber daya selain memori).
Greg Rogers
7
Ini adalah argumen palsu. Karena C ++ memiliki manajemen memori eksplisit, Anda perlu mencari tahu kapan setiap objek harus dibebaskan. Dengan GC, itu tidak lebih buruk; alih-alih, masalahnya dikurangi menjadi mencari tahu kapan objek tertentu dibebaskan, yaitu objek yang memerlukan pertimbangan khusus pada penghapusan. Pengalaman pemrograman di Java dan C # mengungkapkan bahwa sebagian besar objek tidak memerlukan pertimbangan khusus dan dapat dengan aman diserahkan kepada GC. Ternyata, salah satu fungsi utama destruktor di C ++ adalah untuk membebaskan objek anak, yang ditangani oleh GC untuk Anda secara otomatis.
Nate CK
2
@ NateC-K: Satu hal yang ditingkatkan dalam GC vs non-GC (mungkin yang terbesar) adalah kemampuan sistem GC yang solid untuk menjamin bahwa setiap referensi akan terus menunjuk ke objek yang sama selama referensi tersebut ada. Memanggil Disposeobjek 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.
supercat
22

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:

[Catatan: Untuk program pengumpulan sampah, implementasi yang di-host dengan kualitas tinggi harus berupaya memaksimalkan jumlah memori yang tidak terjangkau yang diklaimnya kembali. —Kirim catatan]

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.

Jerry Coffin
sumber
Saya berpendapat bahwa meskipun seseorang menentukan bahwa untuk operasi yang tepat semua objek harus dihapus, dan hanya objek yang telah dihapus yang akan memenuhi syarat untuk pengumpulan, dukungan kompiler untuk pengumpulan sampah pelacakan referensi masih bisa berguna, karena bahasa seperti itu dapat memastikan bahwa penggunaan pointer yang dihapus (referensi) akan dijamin menjebak, alih-alih menyebabkan Perilaku Tidak Terdefinisi.
supercat
2
Bahkan di Jawa, GC tidak benar-benar ditentukan untuk melakukan sesuatu yang berguna AFAIK. Ini mungkin memanggil freeAnda (di mana saya bermaksud freeanalagous 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 menelepon close()sepanjang waktu dan mereka harus sangat menyadari manajemen sumber daya, berhati-hati untuk tidak menelepon close()terlalu cepat atau terlambat. C ++ membebaskan kita dari itu. ... (lanjutan)
Aaron McDaid
2
.. komentar saya beberapa saat yang lalu tidak dimaksudkan untuk mengkritik Java. Saya hanya mengamati bahwa istilah "pengumpulan sampah" adalah istilah yang sangat aneh - artinya jauh lebih sedikit daripada yang dipikirkan orang dan karena itu sulit untuk membahasnya tanpa jelas apa artinya.
Aaron McDaid
@AaronMcDaid Memang benar bahwa GC tidak membantu dengan sumber daya non-memori sama sekali. Untungnya, sumber daya seperti itu jarang dialokasikan jika dibandingkan dengan memori. Selain itu, lebih dari 90% dari mereka dapat dibebaskan dalam metode yang mengalokasikannya, jadi try (Whatever w=...) {...}selesaikan (dan Anda mendapat peringatan saat Anda lupa). Yang tersisa juga bermasalah dengan RAII. Memanggil close()"sepanjang waktu" berarti mungkin sekali per puluhan ribu baris, jadi itu tidak terlalu buruk, sementara memori dialokasikan hampir di setiap baris Java.
maaartinus
15

Jika Anda ingin pengumpulan sampah otomatis, ada pengumpul sampah komersial dan domain publik yang bagus untuk C ++. Untuk aplikasi di mana pengumpulan sampah cocok, C ++ adalah bahasa sampah yang dikumpulkan dengan kinerja yang sangat baik dibandingkan dengan bahasa sampah lainnya. Lihat Bahasa Pemrograman C ++ (Edisi 4) untuk diskusi tentang pengumpulan sampah otomatis di C ++. Lihat juga, Hans-J. Situs Boehm untuk pengumpulan sampah C dan C ++ ( arsip ).

Juga, C ++ mendukung teknik pemrograman yang memungkinkan manajemen memori menjadi aman dan implisit tanpa pengumpul sampah . Saya menganggap pengumpulan sampah sebagai pilihan terakhir dan cara penanganan yang tidak sempurna untuk pengelolaan sumber daya. Itu tidak berarti bahwa itu tidak pernah berguna, hanya saja ada pendekatan yang lebih baik dalam banyak situasi.

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.

Rayne
sumber
"dengan kinerja yang sebanding dengan sampah yang dikumpulkan bahasa lainnya". Kutipan?
Jon Harrop
1
Tautan saya rusak. Saya menulis jawaban ini 5 tahun yang lalu.
Rayne
1
Ok, saya berharap untuk verifikasi independen dari klaim ini, yaitu bukan oleh Stroustrup atau Boehm. :-)
Jon Harrop
12

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:

void f(int n, int x) {
    Gadget *p = new Gadget{n};
    if(x<100) throw SomeException{};
    if(x<200) return;
    delete p;
}

Ini tidak aman di C ++. Tetapi juga tidak aman di Jawa! Dalam C ++, jika fungsi kembali lebih awal, deletetidak 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:

void f(int n, int x) {
    Gadget p = {n};  // Just leave it on the stack (where it belongs!)
    if(x<100) throw SomeException{};
    if(x<200) return;
}

Ini membutuhkan lebih sedikit karakter untuk diketik. Tidak ada newyang menghalangi. Anda tidak perlu mengetik Gadgetdua kali. Objek dihancurkan di akhir fungsi. Jika ini yang Anda inginkan, ini sangat intuitif. GadgetBerperilaku sama dengan intatau double. 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 menggunakanshared_ptr misalnya. (Atau mengembalikan objek (besar) dengan nilai, meningkatkan gerakan-semantik atau unique_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 gagal shared_ptr,. C ++ 11 yang ditulis dengan baik lebih pendek, lebih mudah dibaca, dan lebih mudah diajarkan daripada banyak bahasa lain dalam hal manajemen memori.

Aaron McDaid
sumber
1
GC hanya boleh digunakan untuk objek yang tidak memperoleh sumber daya (yaitu meminta entitas lain untuk melakukan hal-hal atas nama mereka "hingga pemberitahuan lebih lanjut"). Jika Gadgettidak meminta hal lain untuk melakukan apa pun atas namanya, kode asli akan sepenuhnya aman di Jawa jika pernyataan yang tidak bermakna (ke Jawa) deletedihapus.
supercat
@ supercat, objek dengan destruktor yang membosankan menarik. (Saya belum mendefinisikan 'membosankan', tetapi pada dasarnya destruktor yang tidak perlu dipanggil, kecuali untuk membebaskan memori). Mungkin saja bagi kompiler individual untuk memperlakukan shared_ptr<T>secara khusus ketika T'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. A shared_ptrhanya dapat dilihat sebagai pointer GC, untuk yang sesuai T. Tetapi ada batasan dalam hal ini, dan itu akan membuat banyak program lebih lambat.
Aaron McDaid
Sistem tipe yang baik harus memiliki tipe yang berbeda untuk objek heap yang dikelola oleh GC dan RAII, karena beberapa pola penggunaan bekerja sangat baik dengan satu dan sangat buruk dengan yang lain. Di .NET atau Java, pernyataan 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 ketika string2sedang sedang ditulis, string1akan memiliki nilai lama atau nilai baru, tanpa Perilaku Tidak Terdefinisi).
supercat
Dalam C ++, penugasan a shared_ptr<String>membutuhkan banyak sinkronisasi di belakang layar, dan penugasan a Stringdapat berperilaku aneh jika suatu variabel dibaca dan ditulis secara bersamaan. Kasus di mana seseorang ingin menulis dan membaca secara Stringbersamaan 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".
supercat
1
@curiousguy tidak ada yang berubah, kecuali jika Anda mengambil tindakan pencegahan yang tepat, Java masih memungkinkan finalizer dipanggil segera setelah konstruktor selesai. Berikut contoh kehidupan nyata: " finalize () meminta objek yang sangat dapat dijangkau di Java 8 ". Kesimpulannya adalah untuk tidak pernah menggunakan fitur ini, bahwa hampir semua orang setuju untuk menjadi kesalahan desain historis bahasa. Ketika kita mengikuti saran itu, bahasa memberikan determinisme yang kita sukai.
Holger
11

Karena C ++ modern tidak memerlukan pengumpulan sampah.

Jawaban FAQ Bjarne Stroustrup tentang masalah ini mengatakan :

Saya tidak suka sampah. Saya tidak suka membuang sampah sembarangan. Cita-cita saya adalah menghilangkan kebutuhan akan pemulung dengan tidak menghasilkan sampah. Itu sekarang mungkin.


Situasi, untuk kode yang ditulis hari ini (C ++ 17 dan mengikuti Pedoman Inti resmi ) adalah sebagai berikut:

  • Sebagian besar kode yang terkait dengan kepemilikan memori ada di perpustakaan (terutama yang menyediakan wadah).
  • Sebagian besar penggunaan kode yang melibatkan kepemilikan memori mengikuti pola RAII , jadi alokasi dibuat pada konstruksi dan deallokasi pada kehancuran, yang terjadi ketika keluar dari ruang lingkup di mana sesuatu dialokasikan.
  • Anda tidak secara eksplisit mengalokasikan atau membatalkan alokasi memori secara langsung .
  • Pointer mentah tidak memiliki memori (jika Anda telah mengikuti pedoman), jadi Anda tidak dapat membocorkannya dengan membagikannya.
  • Jika Anda bertanya-tanya bagaimana Anda akan meneruskan alamat awal urutan nilai dalam memori - Anda akan melakukannya dengan rentang ; tidak membutuhkan pointer mentah.
  • Jika Anda benar-benar membutuhkan "pointer" yang dimiliki, Anda menggunakan pointer pintar pustaka standar C ++ ' - mereka tidak dapat bocor, dan cukup efisien (meskipun ABI dapat menghalangi hal itu). Atau, Anda dapat melewati kepemilikan melintasi batas ruang lingkup dengan "pointer pemilik" . Ini tidak umum dan harus digunakan secara eksplisit; tetapi ketika diadopsi - mereka memungkinkan pemeriksaan statis yang bagus terhadap kebocoran.

"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:

  1. Anda jarang melakukan ini (dalam hal tempat dalam kode, tidak harus dalam hal fraksi waktu eksekusi)
  2. Anda hanya akan melakukan ini dengan sengaja, bukan secara tidak sengaja.
  3. Melakukannya akan menonjol dalam basis kode yang sesuai dengan pedoman.
  4. Ini semacam kode tempat Anda akan mem-bypass GC dalam bahasa lain.

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

einpoklum
sumber
Ya, memang (perlu GC) jika Anda menggiling string .. Bayangkan Anda memiliki array string yang besar (pikirkan ratusan megabita) bahwa Anda sedang membangun sedikit demi sedikit, kemudian memproses dan membangun kembali ke panjang yang berbeda, menghapus yang tidak digunakan, menggabungkan yang lain, dll. tahu karena saya harus beralih ke bahasa tingkat tinggi untuk mengatasinya. (Tentu saja Anda bisa membangun GC sendiri juga).
www-0av-Com
2
@ user1863152: Itu adalah kasus di mana pengalokasi khusus akan berguna. Itu masih tidak memerlukan GC bahasa-integral ...
einpoklum
to einpoklum: benar. Ini hanya kuda untuk kursus. Syarat saya adalah memproses galon yang berubah secara dinamis dari Informasi Penumpang Transportasi. Subjek yang menarik .. Benar-benar datang ke filosofi perangkat lunak.
www-0av-Com
GC sebagai dunia Java dan .NET telah menemukan akhirnya memiliki masalah besar - tidak skala. Ketika Anda memiliki milyaran objek hidup dalam memori seperti yang kita lakukan hari ini dengan perangkat lunak non-sepele, Anda harus mulai menulis kode untuk menyembunyikan sesuatu dari GC. Merupakan beban untuk memiliki GC di Java dan .NET.
Zach Saw
10

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.

Uri
sumber
2
Saya tidak berpikir GC membutuhkan VM. Kompiler dapat menambahkan kode ke semua operasi penunjuk untuk memperbarui status global, sementara utas terpisah berjalan di latar belakang menghapus objek sesuai kebutuhan.
user83255
3
Saya setuju. Anda tidak memerlukan mesin virtual, tetapi begitu Anda mulai memiliki sesuatu yang mengatur memori Anda untuk Anda seperti itu di latar belakang, saya merasa bahwa Anda telah meninggalkan "kabel listrik" yang sebenarnya dan memiliki semacam situasi VM.
Uri
8

Untuk menjawab sebagian besar pertanyaan "mengapa" tentang C ++, baca Desain dan Evolusi C ++

Nemanja Trifunovic
sumber
4

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,

supercat
sumber
Itu adalah poin yang sangat bagus, saya baru-baru ini mencuri beberapa bit dari pointer saya karena jika tidak akan ada jumlah cache yang meleset.
Passer By
@ PaserBy: Saya bertanya-tanya berapa banyak aplikasi yang menggunakan pointer 64-bit akan mendapat manfaat lebih dari menggunakan pointer skala 32-bit sebagai referensi objek, atau menyimpan hampir semuanya dalam 4GiB ruang alamat dan menggunakan objek khusus untuk menyimpan / mengambil data dari tinggi -kecepatan penyimpanan di luar? Mesin memiliki RAM yang cukup sehingga konsumsi RAM pointer 64-bit mungkin tidak masalah, kecuali bahwa mereka melahap cache dua kali lebih banyak dari pointer 32-bit.
supercat
3

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.

Bob Holmes
sumber
3

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.

Sqandr
sumber
3

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.

Mike76
sumber
1
Membebaskan memori dan menjalankan destruktor adalah masalah yang terlalu terpisah. (Java tidak memiliki destruktor, yang merupakan PITA.) GC membebaskan memori, ia tidak menjalankan dtor.
curiousguy
0

Terutama karena dua alasan:

  1. Karena tidak perlu satu (IMHO)
  2. Karena itu sangat tidak kompatibel dengan RAII, yang merupakan landasan C ++

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 ++.

Marc Coll
sumber
Ada banyak (baru) algoritma yang secara inheren sulit diimplementasikan tanpa pengumpulan sampah. Waktu terus berjalan. Inovasi juga datang dari wawasan baru yang cocok dengan bahasa (pengumpulan sampah) tingkat tinggi. Cobalah untuk backport semua ini ke GC gratis C ++, Anda akan melihat benjolan di jalan. (Saya tahu saya harus memberikan contoh, tapi saya agak terburu-buru sekarang. Maaf. Yang bisa saya pikirkan sekarang berkisar pada struktur data yang persisten, di mana penghitungan referensi tidak akan berhasil.).
BitTickler
0

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.

www-0av-Com
sumber