Mengapa Pengumpulan Sampah jika ada pointer pintar

67

Saat ini, banyak sekali bahasa yang dikumpulkan dari sampah. Bahkan tersedia untuk C ++ oleh pihak ketiga. Tetapi C ++ memiliki RAII dan pointer pintar. Jadi apa gunanya menggunakan pengumpulan sampah? Apakah itu melakukan sesuatu yang ekstra?

Dan dalam bahasa lain seperti C #, jika semua referensi diperlakukan sebagai smart pointer (menjauhkan RAII), dengan spesifikasi dan implementasi, apakah masih akan ada kebutuhan pemulung? Jika tidak, mengapa ini tidak terjadi?

Gulshan
sumber
1
Satu hal yang saya pahami setelah mengajukan pertanyaan ini - Smart pointer membutuhkan RAII untuk mengelola deallokasi otomatis .
Gulshan
8
Pointer pintar berarti menggunakan RAII untuk GC;)
Dario
Heh, c # harus memiliki opsi untuk menangani semua "pengumpulan sampah" dengan RAII. Referensi melingkar dapat dideteksi pada aplikasi shutdown, yang kita butuhkan adalah melihat alokasi mana yang masih ada dalam memori setelah kelas Program.cs telah di-deallocated. Kemudian referensi melingkar dapat diganti dengan semacam referensi minggu.
AareP

Jawaban:

67

Jadi, apa gunanya menggunakan pengumpulan sampah?

Saya berasumsi maksud Anda referensi dihitung pointer pintar dan saya akan mencatat bahwa mereka adalah bentuk (sampah) pengumpulan sampah jadi saya akan menjawab pertanyaan "apa keuntungan dari bentuk pengumpulan sampah lainnya dibandingkan referensi dihitung pointer pintar" sebagai gantinya.

  • Akurasi . Referensi menghitung sendiri kebocoran siklus sehingga referensi yang dihitung pointer pintar akan membocorkan memori secara umum kecuali jika teknik lain ditambahkan untuk menangkap siklus. Setelah teknik-teknik itu ditambahkan, manfaat penghitungan referensi dari kesederhanaan telah sirna. Juga, perhatikan bahwa penghitungan referensi berbasis ruang lingkup dan penelusuran GCs mengumpulkan nilai pada waktu yang berbeda, terkadang penghitungan referensi mengumpulkan lebih awal dan kadang-kadang pelacakan GC dikumpulkan lebih awal.

  • Throughput . Pointer pintar adalah salah satu bentuk pengumpulan sampah yang paling tidak efisien, khususnya dalam konteks aplikasi multi-utas ketika jumlah referensi ditabrak secara atom. Ada teknik penghitungan referensi canggih yang dirancang untuk meringankan ini tetapi melacak GC masih merupakan algoritma pilihan di lingkungan produksi.

  • Latensi . Implementasi smart pointer yang khas memungkinkan destructors untuk longsoran, menghasilkan waktu jeda yang tidak terbatas. Bentuk pengumpulan sampah lainnya jauh lebih bertahap dan bahkan dapat waktu nyata, misalnya treadmill milik Baker.

Jon Harrop
sumber
23
Tidak percaya jawaban ini akhirnya menjadi jawaban teratas. Ini menunjukkan kurangnya pemahaman tentang C ++ smart pointer dan membuat klaim yang sangat tidak sinkron dengan kenyataan yang hanya konyol. Pertama, penunjuk cerdas yang dalam bagian kode C ++ yang dirancang dengan baik akan paling dominan adalah penunjuk unik, bukan penunjuk saham. en.cppreference.com/w/cpp/memory/unique_ptr Dan kedua, saya tidak percaya Anda benar-benar mengklaim keuntungan 'kinerja' dan keuntungan real-time dari pengumpulan sampah non deterministik atas pointer pintar.
user1703394
4
@ user1703394 Tampaknya penjawab telah membagikan pointer dalam pikiran (benar atau salah, saya tidak yakin apa yang disarankan OP), yang kurang berkinerja daripada pengumpulan sampah non deterministik.
Nathan Cooper
8
Ini semua adalah argumen orang bodoh dan hanya valid jika Anda benar-benar mengabaikan pertanyaan aktual atau mengabaikan pola penggunaan aktual dari berbagai jenis smart pointer. Pertanyaannya adalah tentang petunjuk cerdas. Ya shared_ptr adalah penunjuk pintar dan ya shared_ptr adalah penunjuk pintar paling mahal, tapi tidak, tidak ada argumen aktual untuk penggunaannya yang luas di mana pun dekat untuk membuat argumen kinerja apa pun dekat dengan yang relevan. Serius, jawaban ini harus dipindahkan ke pertanyaan tentang penghitungan referensi. Iso jawaban penghitungan referensi miskin untuk pertanyaan pointer cerdas yang baik.
user1703394
4
"Smart pointer bukan konsep yang lebih luas", Serius? Anda tidak tahu seberapa jauh pernyataan itu merongrong semua argumen yang mungkin Anda buat sejauh ini. Mungkin melihat kepemilikan Rust dan memindahkan semantik: koerbitz.me/posts/... Sangat mudah bagi orang-orang dengan pengalaman bersejarah dengan C ++ untuk melewatkan fakta bahwa C ++ 11 / C ++ 14 dengan model memorinya, ditingkatkan pintar pointer dan memindahkan semantik adalah binatang yang sama sekali berbeda dari pendahulunya. Lihat bagaimana Rust melakukannya, ia lebih bersih daripada C ++ dan memberikan perspektif baru.
user1703394
6
@ user1703394: "Jeda tak terbatas karena destruktor adalah properti yang disayangkan RAII digunakan untuk sumber daya non-memori". Tidak, ini tidak ada hubungannya dengan sumber daya non-memori.
Jon Harrop
63

Karena tidak ada yang melihatnya dari sudut ini, saya akan ulangi pertanyaan Anda: mengapa memasukkan sesuatu ke dalam bahasa jika Anda bisa melakukannya di perpustakaan? Mengabaikan implementasi spesifik dan detail sintaksis, petunjuk GC / smart pada dasarnya adalah kasus khusus dari pertanyaan itu. Mengapa mendefinisikan pengumpul sampah dalam bahasa itu sendiri jika Anda dapat menerapkannya di perpustakaan?

Ada beberapa jawaban untuk pertanyaan itu. Yang terpenting pertama:

  1. Anda memastikan bahwa semua kode dapat menggunakannya untuk beroperasi. Hal ini, saya pikir, yang alasan besar mengapa menggunakan kembali kode dan berbagi kode tidak benar-benar lepas landas sampai Java / C # / Python / Ruby. Perpustakaan perlu berkomunikasi, dan satu-satunya bahasa bersama yang dapat diandalkan yang mereka miliki adalah apa yang ada dalam spesifikasi bahasa itu sendiri (dan, pada tingkat tertentu, perpustakaan standarnya). Jika Anda pernah mencoba menggunakan kembali pustaka di C ++, kemungkinan Anda mengalami rasa sakit luar biasa yang tidak disebabkan oleh semantik memori standar. Saya ingin meneruskan sebuah struct ke beberapa lib. Apakah saya lulus referensi? Pointer? scoped_ptr?smart_ptr? Apakah saya lulus kepemilikan, atau tidak? Apakah ada cara untuk menunjukkan itu? Bagaimana jika lib perlu dialokasikan? Apakah saya harus memberinya pengalokasi? Dengan tidak menjadikan manajemen memori bagian dari bahasa, C ++ memaksa setiap pasangan perpustakaan untuk menegosiasikan strategi spesifik mereka di sini, dan sangat sulit untuk membuat mereka semua setuju. GC menjadikan itu sepenuhnya tidak menjadi masalah.

  2. Anda dapat mendesain sintaks di sekitarnya. Karena C ++ tidak merangkum manajemen memori itu sendiri, itu harus menyediakan berbagai kait sintaksis untuk membiarkan kode tingkat pengguna mengekspresikan semua detail. Anda memiliki pointer, referensi,, constoperator dereferencing, operator tidak langsung, alamat, dll. Jika Anda memasukkan manajemen memori ke dalam bahasa itu sendiri, sintaksis dapat dirancang sekitar itu. Semua operator itu hilang dan bahasa menjadi lebih bersih dan sederhana.

  3. Anda mendapatkan pengembalian investasi yang tinggi. Nilai yang dihasilkan oleh setiap potongan kode dikalikan dengan jumlah orang yang menggunakannya. Ini berarti bahwa semakin banyak pengguna yang Anda miliki, semakin banyak yang mampu Anda keluarkan untuk sebuah perangkat lunak. Saat Anda memindahkan fitur ke dalam bahasa, semua pengguna bahasa akan menggunakannya. Ini berarti Anda dapat mengalokasikan lebih banyak upaya untuk itu daripada yang Anda bisa ke perpustakaan hanya digunakan oleh subset dari pengguna tersebut. Inilah sebabnya mengapa bahasa seperti Java dan C # memiliki VM yang benar-benar kelas satu dan pengumpul sampah berkualitas tinggi yang fantastis: biaya pengembangannya diamortisasi pada jutaan pengguna.

banyak sekali
sumber
Jawaban yang fantastis! Andai saja saya dapat memilih lebih dari satu kali ...
Dean Harding
10
Perlu dicatat bahwa pengumpulan sampah tidak benar-benar diimplementasikan dalam bahasa C # itu sendiri, tetapi dalam .NET Framework , khususnya Common Language Runtime (CLR).
Robert Harvey
6
@RobertHarvey: Ini tidak diterapkan oleh bahasa, tetapi itu tidak akan berhasil tanpa kerja sama dari bahasa tersebut. Sebagai contoh, kompiler harus menyertakan informasi yang menentukan, pada setiap titik dalam kode, lokasi setiap register atau offset tumpukan-bingkai yang menyimpan referensi ke objek yang tidak dipipatkan. Itu adalah mutlak tanpa pengecualian - apa pun yang invarian , yang tidak dapat ditegakkan tanpa dukungan bahasa.
supercat
Keuntungan utama memiliki dukungan GC bahasa dan kerangka kerja yang dibutuhkan adalah memastikan bahwa tidak ada referensi yang pernah ada untuk memori yang mungkin dialokasikan untuk beberapa tujuan lain. Jika seseorang memanggil Disposeobjek yang merangkum bitmap, referensi apa pun ke objek itu akan menjadi referensi ke objek bitmap yang dibuang. Jika objek dihapus sebelum waktunya sementara kode lain masih berharap untuk menggunakannya, kelas bitmap dapat memastikan bahwa kode lain akan gagal dengan cara yang dapat diprediksi . Sebaliknya, menggunakan referensi ke memori yang dibebaskan adalah Perilaku Tidak Terdefinisi.
supercat
34

Pengumpulan sampah pada dasarnya hanya berarti bahwa objek yang dialokasikan Anda secara otomatis dirilis pada beberapa titik setelah mereka tidak dapat dijangkau lagi.

Lebih tepatnya, mereka dilepaskan ketika mereka menjadi tidak terjangkau untuk program ini, karena objek yang dirujuk secara melingkar tidak akan pernah dirilis sebaliknya.

Pointer pintar hanya merujuk pada struktur apa pun yang berperilaku seperti pointer biasa tetapi memiliki beberapa fungsi tambahan yang terpasang. Ini termasuk tetapi tidak terbatas pada deallokasi, tetapi juga copy-on-write, cek terikat, ...

Sekarang, seperti yang telah Anda nyatakan, smart pointer dapat digunakan untuk mengimplementasikan bentuk pengumpulan sampah.

Tetapi alur pemikiran berjalan seperti berikut:

  1. Pengumpulan sampah adalah hal yang keren untuk dimiliki, karena nyaman dan saya harus mengurus lebih sedikit barang
  2. Karena itu: Saya ingin pengumpulan sampah dalam bahasa saya
  3. Sekarang, bagaimana cara memasukkan GC ke dalam bahasa saya?

Tentu saja, Anda dapat mendesainnya seperti ini dari awal. C # dirancang untuk menjadi sampah yang dikumpulkan, jadi hanya newobjek Anda dan itu akan dirilis ketika referensi keluar dari ruang lingkup. Cara ini dilakukan terserah kompiler.

Namun di C ++, tidak ada pengumpulan sampah yang dimaksudkan. Jika kita mengalokasikan beberapa pointer int* p = new int;dan jatuh keluar dari ruang lingkup, pitu sendiri dihapus dari stack, tetapi tidak ada yang mengurus memori yang dialokasikan.

Sekarang satu-satunya yang Anda miliki dari awal adalah destruktor deterministik . Ketika suatu objek meninggalkan ruang lingkup yang telah dibuat, destruktornya disebut. Dalam kombinasi dengan templat dan overloading operator, Anda bisa mendesain objek pembungkus yang berperilaku seperti pointer, tetapi menggunakan fungsionalitas destruktor untuk membersihkan sumber daya yang melekat padanya (RAII). Anda menyebut ini penunjuk cerdas .

Ini semua sangat spesifik C ++: Overloading operator, templat, destruktor, ... Dalam situasi bahasa khusus ini, Anda telah mengembangkan pointer cerdas untuk memberi Anda GC yang Anda inginkan.

Tetapi jika Anda merancang bahasa dengan GC dari awal, ini hanyalah detail implementasi. Anda hanya mengatakan objek akan dibersihkan dan kompiler akan melakukan ini untuk Anda.

Pointer pintar seperti dalam C ++ tidak mungkin bahkan mungkin dalam bahasa seperti C #, yang tidak memiliki kehancuran deterministik sama sekali (C # bekerja di sekitar ini dengan menyediakan gula sintaksis untuk memanggil .Dispose()objek tertentu). Sumber daya yang tidak direferensikan akhirnya akan direklamasi oleh GC, tetapi tidak ditentukan kapan tepatnya ini akan terjadi.

Dan ini, pada gilirannya, dapat memungkinkan GC melakukan tugasnya dengan lebih efisien. Dibangun lebih dalam ke dalam bahasa daripada smart pointer, yang ditetapkan di atasnya, .NET GC misalnya dapat menunda operasi memori dan melakukan mereka dalam blok untuk membuatnya lebih murah atau bahkan memindahkan memori untuk meningkatkan efisiensi berdasarkan seberapa sering objek diakses.

Dario
sumber
C # memang memiliki bentuk kehancuran deterministik melalui IDisposabledan using. Tetapi membutuhkan sedikit upaya programmer, itulah sebabnya biasanya hanya digunakan untuk sumber daya yang sangat langka seperti pegangan koneksi database.
JSB ձոգչ
7
@ JSBangs: Tepat. Sama seperti C ++ membangun pointer pintar di sekitar RAII untuk mendapatkan GC, C # sebaliknya dan membangun "smart disposers" di sekitar GC untuk mendapatkan RAII;) Faktanya, sangat disayangkan bahwa RAII sangat sulit di C # karena bagus untuk pengecualian- penanganan sumber daya yang aman. F # misalnya mencoba IDisposablesintaksis yang lebih sederhana dengan hanya mengganti konvensional let ident = valuedengan use ident = value...
Dario
@Dario: "C # pergi ke arah lain dan membangun 'smart disposers' di sekitar GC untuk mendapatkan RAII". RAII di C # dengan usingtidak ada hubungannya dengan pengumpulan sampah sama sekali, itu hanya memanggil fungsi ketika variabel jatuh keluar dari ruang lingkup seperti destruktor di C ++.
Jon Harrop
1
@ Jon Harrop: Silakan apa? Pernyataan yang Anda kutip adalah tentang destruktor C ++ biasa, tanpa melibatkan penghitungan referensi / smart pointer / pengumpulan sampah.
Dario
1
"Pengumpulan sampah pada dasarnya hanya berarti bahwa objek yang dialokasikan Anda secara otomatis dirilis ketika mereka tidak direferensikan lagi. Lebih tepatnya, mereka dilepaskan ketika mereka menjadi tidak terjangkau untuk program, karena objek yang direferensikan secara melingkar tidak akan pernah dirilis sebaliknya." ... Lebih akurat adalah dengan mengatakan bahwa mereka secara otomatis dirilis di beberapa titik setelahnya , bukan kapan . Perhatikan bahwa ketika menyiratkan bahwa reklamasi terjadi segera, padahal sebenarnya reklamasi sering terjadi jauh kemudian.
Todd Lehman
4

Menurut saya, ada dua perbedaan besar antara pengumpulan sampah dan petunjuk pintar yang digunakan untuk manajemen memori:

  1. Pointer pintar tidak dapat mengumpulkan sampah siklik; pengumpulan sampah bisa
  2. Pointer pintar melakukan semua pekerjaan pada saat referensi, dereferencing, dan deallokasi, pada utas aplikasi; pengumpulan sampah tidak perlu

Yang pertama berarti bahwa GC akan mengumpulkan sampah yang pointer pintar tidak akan; jika Anda menggunakan smart pointer, Anda harus menghindari membuat sampah semacam ini, atau bersiap untuk menanganinya secara manual.

Yang terakhir berarti bahwa tidak peduli seberapa pintar pointer cerdas, operasi mereka akan memperlambat utas dalam program Anda. Pengumpulan sampah dapat menunda pekerjaan, dan memindahkannya ke utas lainnya; yang membuatnya menjadi lebih efisien secara keseluruhan (memang, biaya runtime dari GC modern kurang dari sistem malloc / bebas normal, bahkan tanpa overhead tambahan dari smart pointer), dan melakukan pekerjaan apa yang masih perlu dilakukan tanpa masuk ke dalam cara utas aplikasi.

Sekarang, perhatikan bahwa smart pointer, yang merupakan konstruksi terprogram, dapat digunakan untuk melakukan segala macam hal menarik lainnya - lihat jawaban Dario - yang sepenuhnya berada di luar ruang lingkup pengumpulan sampah. Jika Anda ingin melakukan itu, Anda akan memerlukan smart pointer.

Namun, untuk keperluan manajemen memori, saya tidak melihat adanya prospek smart pointer menggantikan pengumpulan sampah. Mereka sama sekali tidak ahli dalam hal itu.

Tom Anderson
sumber
6
@ Tom: lihat jawaban Dario untuk detail tentang petunjuk pintar. Adapun keuntungan dari pointer cerdas - deinokasi deterministik dapat menjadi keuntungan besar ketika digunakan untuk mengontrol sumber daya (tidak hanya memori). Bahkan, ini telah terbukti sangat penting sehingga Microsoft telah memperkenalkan usingblok dalam versi C # berikutnya. Selain itu, perilaku nondeterministik dari GC dapat dilarang dalam sistem waktu nyata (itulah sebabnya mengapa GC tidak digunakan di sana). Juga, jangan lupa bahwa GC sangat rumit untuk diperbaiki sehingga sebagian besar sebenarnya bocor memori dan sangat tidak efisien (misalnya Boehm ...).
Konrad Rudolph
6
Ketidakpastian GC adalah, saya pikir, sedikit herring merah - ada sistem GC yang cocok untuk penggunaan waktu nyata (seperti IBM Recycler), meskipun yang Anda lihat di VM desktop dan server tidak. Plus, menggunakan pointer pintar berarti menggunakan malloc / gratis, dan implementasi konvensional malloc tidak bersifat deterministik karena kebutuhan untuk mencari daftar gratis. Memindahkan sistem GC memiliki waktu alokasi yang lebih deterministik daripada sistem malloc / free, meskipun tentu saja waktu deallokasi yang kurang deterministik.
Tom Anderson
3
Adapun kompleksitas: ya, GC itu kompleks, tetapi saya tidak sadar bahwa "sebenarnya memori bocor dan sangat tidak efisien", dan akan tertarik untuk melihat beberapa bukti sebaliknya. Boehm bukan bukti, karena ini adalah implementasi yang sangat primitif, dan itu dibangun untuk melayani bahasa, C, di mana GC akurat pada dasarnya tidak mungkin karena kurangnya jenis keamanan. Ini adalah upaya yang berani, dan itu berhasil sama sekali sangat mengesankan, tetapi Anda tidak dapat menganggapnya sebagai contoh dari GC.
Tom Anderson
8
@ Jon: jelas bukan omong kosong. bugzilla.novell.com/show_bug.cgi?id=621899 atau, lebih umum: flyingfrogblog.blogspot.com/2009/01/... Ini terkenal dan merupakan properti dari semua GC yang konservatif.
Konrad Rudolph
3
"Biaya runtime dari GC modern kurang dari sistem malloc / bebas normal." Ikan haring merah di sini. Ini hanya karena malloc tradisional adalah algoritma yang sangat tidak efisien. Pengalokasi modern yang menggunakan banyak bucket untuk ukuran blok yang berbeda jauh lebih cepat untuk dialokasikan, jauh lebih rentan terhadap tumpukan fragmentasi, dan masih memberi Anda deallokasi cepat.
Mason Wheeler
3

Istilah pengumpulan sampah berarti ada sampah yang harus dikumpulkan. Dalam C ++ pointer cerdas datang dalam berbagai rasa, yang terpenting adalah unique_ptr. Unique_ptr pada dasarnya adalah kepemilikan tunggal dan konstruksi pelingkupan. Dalam bagian kode yang dirancang dengan baik, sebagian besar tumpukan item yang dialokasikan biasanya berada di belakang pointer smart unique_ptr dan kepemilikan sumber daya tersebut akan didefinisikan dengan baik setiap saat. Nyaris tidak ada overhead dalam unique_ptr dan unique_ptr menghilangkan sebagian besar masalah manajemen memori manual yang secara tradisional mengarahkan orang ke bahasa yang dikelola. Sekarang karena lebih banyak core yang berjalan bersamaan menjadi barang yang lebih umum, prinsip-prinsip desain yang mendorong kode untuk menggunakan kepemilikan yang unik dan terdefinisi dengan baik pada setiap saat menjadi lebih penting untuk kinerja.

Bahkan dalam program yang dirancang dengan baik, terutama di lingkungan multi-threaded, tidak semuanya dapat diekspresikan tanpa struktur data bersama, dan untuk struktur data yang benar-benar membutuhkan, thread perlu berkomunikasi. RAII di c ++ berfungsi cukup baik untuk masalah seumur hidup dalam satu pengaturan ulir, dalam pengaturan multi-ulir, masa pakai objek mungkin tidak sepenuhnya ditumpuk secara hierarki. Untuk situasi ini, penggunaan shared_ptr menawarkan sebagian besar solusi. Anda membuat kepemilikan bersama atas sumber daya dan ini di C ++ adalah satu-satunya tempat kita melihat sampah, tetapi dalam jumlah kecil sehingga program c ++ yang dirancang dengan baik harus dianggap lebih untuk menerapkan pengumpulan 'sampah' dengan berbagi-ptr daripada pengumpulan sampah lengkap sebagai diimplementasikan dalam bahasa lain. C ++ tidak memiliki 'sampah' sebanyak itu

Seperti yang dinyatakan oleh orang lain, referensi yang dihitung pointer pintar adalah salah satu bentuk pengumpulan sampah, dan untuk itu memiliki satu masalah besar. Contoh yang digunakan sebagian besar sebagai kelemahan referensi bentuk dihitung dari pengumpulan sampah adalah masalah dengan pembuatan struktur data yatim yang terhubung dengan smart pointer satu sama lain yang membuat cluster objek yang menjaga satu sama lain dari yang dikumpulkan. Sementara dalam program yang dirancang sesuai dengan aktor-model perhitungan, struktur data biasanya tidak memungkinkan untuk cluster yang tidak dapat dikumpulkan tersebut muncul di C ++, ketika Anda menggunakan pendekatan data bersama yang luas untuk pemrograman multi-threaded, seperti yang digunakan sebagian besar dari industri, cluster yatim ini dapat dengan cepat menjadi kenyataan.

Jadi untuk meringkas semuanya, jika dengan penggunaan pointer bersama Anda berarti penggunaan unique_ptr yang luas dikombinasikan dengan model aktor dari pendekatan komputasi untuk pemrograman multi-threaded dan penggunaan shared_ptr yang terbatas, daripada bentuk pengumpulan sampah lainnya tidak membelikan Anda apa pun manfaat tambahan. Namun, jika pendekatan segalanya yang dibagikan akan membuat Anda berakhir dengan shared_ptr di semua tempat, daripada Anda harus mempertimbangkan beralih model mata uang atau beralih ke bahasa yang dikelola yang lebih diarahkan pada pembagian kepemilikan yang lebih luas dan akses bersamaan ke struktur data.

pengguna1703394
sumber
1
Apakah ini berarti Rusttidak perlu pengumpulan sampah?
Gulshan
1
@Gulshan Rust adalah salah satu dari sedikit bahasa yang mendukung petunjuk unik yang aman.
CodesInChaos
2

Kebanyakan pointer pintar diimplementasikan menggunakan penghitungan referensi. Artinya, setiap penunjuk pintar yang merujuk ke objek menambah jumlah referensi objek. Ketika hitungan itu menjadi nol, objek dilepaskan.

Masalahnya ada jika Anda memiliki referensi melingkar. Artinya, A memiliki referensi ke B, B memiliki referensi ke C dan C memiliki referensi ke A. Jika Anda menggunakan pointer pintar, maka untuk membebaskan memori yang terkait dengan A, B & C Anda harus secara manual dapatkan di sana "istirahat" referensi melingkar (misalnya menggunakan weak_ptrdalam C ++).

Pengumpulan sampah (biasanya) bekerja sangat berbeda. Sebagian besar pengumpul sampah akhir-akhir ini menggunakan uji jangkauan . Yaitu, ia melihat semua referensi pada stack dan yang dapat diakses secara global dan kemudian melacak setiap objek yang dirujuk oleh referensi tersebut, dan objek yang mereka rujuk, dll. Semua yang lain adalah sampah.

Dengan cara itu, referensi melingkar tidak penting lagi - selama A, B dan C tidak dapat dijangkau , memori dapat direklamasi.

Ada keuntungan lain dari pengumpulan sampah "nyata". Sebagai contoh, alokasi memori sangat murah: hanya menambah pointer ke "ujung" blok memori. Deallokasi juga memiliki biaya diamortisasi yang konstan. Tapi tentu saja bahasa seperti C ++ memungkinkan Anda untuk mengimplementasikan manajemen memori dengan cara apa pun yang Anda suka, sehingga Anda bisa membuat strategi alokasi yang lebih cepat.

Tentu saja, di C ++ jumlah memori yang dialokasikan heap biasanya kurang dari bahasa referensi-berat seperti C # /. NET. Tapi itu bukan masalah pengumpulan sampah vs smart pointer.

Dalam kasus apa pun, masalahnya bukan salah satu yang lebih baik daripada yang lain. Mereka masing-masing memiliki kelebihan dan kekurangan.

Dean Harding
sumber
2

Ini tentang kinerja . Mengosongkan memori membutuhkan banyak administrasi. Jika unallocation berjalan di latar belakang, kinerja proses foreground meningkat. Sayangnya, alokasi memori tidak bisa malas (objek yang dialokasikan akan digunakan pada momen suci berikutnya), tetapi melepaskan objek bisa.

Coba di C ++ (tanpa GC) untuk mengalokasikan banyak objek, cetak "halo", lalu hapus. Anda akan terkejut berapa lama waktu yang dibutuhkan untuk benda bebas.

Juga, GNU libc menyediakan alat yang lebih efektif untuk tidak mengalokasikan memori, lihat rintangan . Harus memperhatikan, saya tidak punya pengalaman dengan rintangan, saya tidak pernah menggunakannya.

ern0
sumber
Pada prinsipnya Anda ada benarnya tetapi harus dicatat bahwa ini adalah masalah yang memiliki solusi yang sangat sederhana: gunakan pengalokasi kumpulan atau pengalokasi objek kecil untuk bundel deallocations. Tapi ini memang membutuhkan (sedikit) lebih banyak upaya daripada memiliki GC berjalan di latar belakang.
Konrad Rudolph
Yap, tentu saja, GC jauh lebih nyaman. (Khusus untuk pemula: tidak ada masalah kepemilikan, bahkan tidak ada operator hapus.)
ern0
3
@ ern0: tidak. Inti dari (penghitungan referensi) pointer pintar adalah bahwa tidak ada masalah kepemilikan dan tidak ada operator hapus.
Konrad Rudolph
3
@ Jon: yang, sejujurnya, sebagian besar waktu. Jika Anda mau tak mau berbagi status objek di antara berbagai utas, Anda akan memiliki masalah yang sama sekali berbeda. Saya akui bahwa banyak orang memprogram seperti itu tetapi ini adalah konsekuensi dari abstraksi threading buruk yang telah ada sampai saat ini dan itu bukan cara yang baik untuk melakukan multithreading.
Konrad Rudolph
1
Deallokasi sering tidak dilakukan "di latar belakang", melainkan menjeda semua utas latar depan. Pengumpulan sampah mode-batch umumnya merupakan kemenangan kinerja, meskipun ada penghentian utas foreground, karena memungkinkan ruang yang tidak terpakai untuk dikonsolidasikan. Seseorang dapat memisahkan proses pengumpulan sampah dan pemadatan tumpukan, tetapi - terutama dalam kerangka kerja yang menggunakan referensi langsung daripada menangani - keduanya cenderung merupakan proses "menghentikan dunia", dan seringkali paling praktis untuk melakukannya bersama.
supercat
2

Pengumpulan sampah bisa lebih efisien - ini pada dasarnya 'menambah' overhead manajemen memori dan melakukan semuanya sekaligus. Secara umum ini akan menghasilkan CPU kurang keseluruhan yang dikeluarkan pada alokasi memori, tetapi itu berarti bahwa Anda akan memiliki ledakan besar aktivitas de-alokasi di beberapa titik. Jika GC tidak dirancang dengan benar, ini dapat menjadi terlihat oleh pengguna sebagai 'jeda' sementara GC mencoba untuk mengalokasikan memori. Sebagian besar GC modern sangat pandai menjaga hal ini tidak terlihat oleh pengguna kecuali dalam kondisi yang paling buruk.

Pointer pintar (atau skema penghitungan referensi) memiliki keuntungan bahwa hal itu terjadi tepat ketika Anda harapkan dari melihat kode (pointer pintar keluar dari ruang lingkup, sesuatu akan dihapus). Anda mendapatkan sedikit de-alokasi di sana-sini. Anda secara keseluruhan dapat menggunakan lebih banyak waktu CPU untuk de-alokasi, tetapi karena itu tersebar di semua hal yang terjadi dalam program Anda, itu lebih kecil kemungkinannya (kecuali de-alokasi beberapa struktur data monster) agar terlihat oleh pengguna Anda.

Jika Anda melakukan sesuatu di mana masalah responsif, saya akan menyarankan bahwa smart pointer / penghitungan memberi tahu Anda secara tepat kapan sesuatu terjadi, sehingga Anda bisa tahu sambil mengkode apa yang mungkin terlihat oleh pengguna Anda. Dalam pengaturan GC Anda hanya memiliki kontrol sesaat atas pengumpul sampah dan hanya perlu mencoba untuk menyelesaikannya.

Di sisi lain, jika throughput keseluruhan adalah tujuan Anda, sistem berbasis GC mungkin merupakan pilihan yang jauh lebih baik, karena meminimalkan sumber daya yang diperlukan untuk melakukan manajemen memori.

Siklus: Saya tidak menganggap masalah siklus sebagai masalah yang signifikan. Dalam sistem di mana Anda memiliki pointer cerdas, Anda cenderung ke arah struktur data yang tidak memiliki siklus, atau Anda hanya berhati-hati tentang bagaimana Anda melepaskan hal-hal seperti itu. Jika perlu, benda penjaga yang tahu cara memutus siklus di benda yang dimiliki dapat digunakan untuk memastikan kerusakan yang tepat secara otomatis. Dalam beberapa bidang pemrograman ini mungkin penting, tetapi untuk sebagian besar pekerjaan sehari-hari, ini tidak relevan.

Michael Kohne
sumber
1
"Anda akan memiliki ledakan besar aktivitas de-alokasi di beberapa titik". Treadmill milik Baker adalah contoh pengumpul sampah yang bertambah indah. memorymanagement.org/glossary/t.html#treadmill
Jon Harrop
1

Keterbatasan dari smart pointer adalah mereka tidak selalu membantu terhadap referensi melingkar. Misalnya Anda memiliki objek A yang menyimpan penunjuk pintar ke objek B dan objek B menyimpan penunjuk pintar ke objek A. Jika dibiarkan bersama tanpa menyetel ulang salah satu pointer, mereka tidak akan pernah dibatalkan alokasi.

Ini terjadi karena penunjuk pintar harus melakukan tindakan tertentu yang tidak akan di-trivia dalam skenario di atas karena kedua objek tidak dapat dijangkau oleh program. Pengumpulan sampah akan mengatasinya - itu akan mengidentifikasi dengan benar bahwa objek tidak dapat dijangkau oleh program dan mereka akan dikumpulkan.

sharptooth
sumber
1

Ini spektrum .

Jika Anda tidak terikat ketat pada kinerja dan siap untuk mengerjakan sesuatu, Anda akan berakhir pada pertemuan atau c, dengan semua tanggung jawab pada Anda untuk membuat keputusan yang tepat dan semua kebebasan untuk melakukan itu, tetapi dengan itu , semua kebebasan untuk mengacaukannya:

"Aku akan memberitahumu apa yang harus dilakukan, lakukan saja. Percayalah padaku".

Pengumpulan sampah adalah ujung lain dari spektrum. Anda memiliki kontrol yang sangat kecil, tetapi diurus untuk Anda:

"Aku akan memberitahumu apa yang aku inginkan, kamu mewujudkannya".

Ini memiliki banyak keuntungan, sebagian besar Anda tidak perlu dapat dipercaya ketika datang untuk mengetahui kapan sumber daya tidak lagi dibutuhkan, tetapi (meskipun beberapa jawaban mengambang di sini) tidak baik untuk kinerja, dan prediktabilitas kinerja. (Seperti semua hal, jika Anda diberi kendali, dan melakukan sesuatu yang bodoh Anda dapat memiliki hasil yang lebih buruk. Namun untuk menyarankan bahwa mengetahui pada waktu kompilasi apa kondisi untuk dapat membebaskan memori, tidak dapat digunakan sebagai kemenangan kinerja adalah melampaui naif).

RAII, pelingkupan, penghitungan ref, dll semuanya adalah pembantu untuk membiarkan Anda bergerak lebih jauh di sepanjang spektrum itu tetapi tidak semua jalan di sana. Semua hal ini masih membutuhkan penggunaan aktif. Mereka masih membiarkan dan mengharuskan Anda untuk berinteraksi dengan manajemen memori dengan cara pengumpulan sampah tidak.

drjpizzle
sumber
0

Harap diingat bahwa pada akhirnya, semuanya bermuara pada instruksi pelaksanaan CPU. Sepengetahuan saya, semua CPU tingkat konsumen memiliki set instruksi yang mengharuskan Anda memiliki data yang disimpan di tempat tertentu dalam memori dan Anda memiliki petunjuk untuk data tersebut. Itu semua yang Anda miliki di tingkat dasar.

Semua yang ada di atas itu dengan pengumpulan sampah, referensi ke data yang mungkin telah dipindahkan, tumpukan pemadatan, dll. Adalah melakukan pekerjaan dalam batasan yang diberikan oleh paradigma "memori sepotong dengan penunjuk alamat" di atas. Hal yang sama dengan pointer pintar - Anda MASIH harus membuat kode berjalan pada perangkat keras yang sebenarnya.


sumber