Yah, saya tahu bahwa ada hal-hal seperti malloc / gratis untuk C, dan baru / menggunakan-a-destruktor untuk manajemen memori dalam C ++, tapi saya bertanya-tanya mengapa tidak ada "pembaruan baru" untuk bahasa-bahasa ini yang memungkinkan pengguna untuk punya opsi untuk mengelola memori secara manual, atau agar sistem melakukannya secara otomatis (pengumpulan sampah)?
Agak dari pertanyaan newb-ish, tetapi hanya berada di CS selama sekitar satu tahun.
garbage-collection
Dark Templar
sumber
sumber
Jawaban:
Pengumpulan sampah membutuhkan struktur data untuk melacak alokasi dan / atau penghitungan referensi. Ini menciptakan overhead dalam memori, kinerja, dan kompleksitas bahasa. C ++ dirancang untuk menjadi "dekat dengan logam", dengan kata lain, dibutuhkan sisi kinerja yang lebih tinggi dari fitur pengorbanan vs kenyamanan. Bahasa lain membuat pengorbanan itu berbeda. Ini adalah salah satu pertimbangan dalam memilih bahasa, yang mana Anda lebih suka.
Yang mengatakan, ada banyak skema untuk penghitungan referensi dalam C ++ yang cukup ringan dan berkinerja, tetapi mereka berada di perpustakaan, baik komersial dan sumber terbuka, bukan bagian dari bahasa itu sendiri. Penghitungan referensi untuk mengelola objek seumur hidup tidak sama dengan pengumpulan sampah, tetapi menangani banyak jenis masalah yang sama, dan lebih cocok dengan pendekatan dasar C ++.
sumber
Sebenarnya, tidak ada manajemen memori sama sekali dalam bahasa C. malloc () dan free () bukan kata kunci dalam bahasa tersebut, tetapi hanya fungsi yang dipanggil dari perpustakaan. Perbedaan ini mungkin terlalu rumit sekarang, karena malloc () dan free () adalah bagian dari pustaka standar C, dan akan disediakan oleh implementasi standar C yang sesuai, tetapi ini tidak selalu benar di masa lalu.
Mengapa Anda ingin bahasa tanpa standar untuk manajemen memori? Ini kembali ke asal C sebagai 'perakitan portabel'. Ada banyak kasus perangkat keras dan algoritma yang dapat mengambil manfaat dari, atau bahkan memerlukan, teknik manajemen memori khusus. Sejauh yang saya tahu, tidak ada cara untuk sepenuhnya menonaktifkan manajemen memori asli Java dan menggantinya dengan Anda sendiri. Ini sama sekali tidak dapat diterima dalam beberapa situasi sumber daya kinerja tinggi / minimal. C memberikan fleksibilitas yang hampir lengkap untuk memilih dengan tepat infrastruktur apa yang akan digunakan oleh program Anda. Harga yang dibayarkan adalah bahwa bahasa C memberikan sedikit bantuan dalam penulisan kode bebas bug yang benar.
sumber
malloc()
ataufree()
. (contohnya adalah MLAP-Compiler untuk PIC)Jawaban sebenarnya adalah bahwa satu-satunya cara untuk membuat mekanisme pengumpulan sampah yang aman dan efisien adalah dengan memiliki dukungan tingkat bahasa untuk referensi yang tidak jelas. (Atau, sebaliknya, kurangnya dukungan tingkat bahasa untuk manipulasi memori langsung.)
Java dan C # dapat melakukannya karena mereka memiliki tipe referensi khusus yang tidak dapat dimanipulasi. Ini memberikan runtime kebebasan untuk melakukan hal-hal seperti memindahkan objek yang dialokasikan dalam memori , yang sangat penting untuk implementasi GC berkinerja tinggi.
Sebagai catatan, tidak ada implementasi GC modern yang menggunakan penghitungan referensi , sehingga benar-benar ikan haring merah. GC modern menggunakan koleksi generasi, di mana alokasi baru diperlakukan pada dasarnya dengan cara yang sama seperti tumpukan alokasi dalam bahasa seperti C ++, dan kemudian secara berkala setiap objek yang baru dialokasikan yang masih hidup dipindahkan ke ruang "selamat" yang terpisah, dan seluruh generasi benda dialokasikan sekaligus.
Pendekatan ini memiliki pro dan kontra: terbalik adalah bahwa tumpukan alokasi dalam bahasa yang mendukung GC secepat alokasi tumpukan dalam bahasa yang tidak mendukung GC, dan downside adalah bahwa objek yang perlu melakukan pembersihan sebelum dihancurkan baik memerlukan mekanisme terpisah (mis.
using
kata kunci C # ) atau kode pembersihannya berjalan non-deterministik.Perhatikan bahwa salah satu kunci untuk GC kinerja tinggi adalah bahwa harus ada dukungan bahasa untuk kelas referensi khusus. C tidak memiliki dukungan bahasa ini dan tidak akan pernah; karena C ++ memiliki overloading operator, ini dapat mengemulasi tipe pointer GC, meskipun itu harus dilakukan dengan hati-hati. Bahkan, ketika Microsoft menemukan dialek C ++ yang akan berjalan di bawah CLR (.NET runtime), mereka harus menciptakan sintaks baru untuk "referensi gaya C #" (mis.
Foo^
) Untuk membedakan mereka dari "referensi gaya C ++" (misFoo&
.).Apa yang dimiliki C ++, dan apa yang secara teratur digunakan oleh para programmer C ++, adalah pointer cerdas , yang sebenarnya hanyalah mekanisme penghitungan referensi. Saya tidak akan menganggap penghitungan referensi sebagai GC "benar", tetapi memberikan banyak manfaat yang sama, dengan biaya kinerja yang lebih lambat daripada manajemen memori manual atau GC benar, tetapi dengan keuntungan dari kerusakan deterministik.
Pada akhirnya, jawabannya benar-benar bermuara pada fitur desain bahasa. C membuat satu pilihan, C ++ membuat pilihan yang memungkinkannya kompatibel dengan C sambil tetap memberikan alternatif yang cukup baik untuk sebagian besar tujuan, dan Java dan C # membuat pilihan lain yang tidak kompatibel dengan C tetapi juga cukup baik untuk sebagian besar tujuan. Sayangnya, tidak ada peluru perak, tetapi terbiasa dengan berbagai pilihan di luar sana akan membantu Anda memilih yang benar untuk program apa pun yang sedang Anda coba buat.
sumber
std::unique_ptr
adalah "dukungan tingkat bahasa untuk referensi yang tidak jelas"? (Itu bukan jenis dukungan yang saya maksud, dan saya juga tidak berpikir itu cukup kecuali dukungan untuk manipulasi memori langsung juga dihapus dari C ++.) Saya memang menyebutkan smart pointer dalam jawaban saya, dan saya akan mempertimbangkanstd:unique_ptr
smart pointer , karena ia benar-benar melakukan penghitungan referensi, itu hanya mendukung kasus-kasus khusus di mana jumlah referensi adalah nol atau satu (danstd::move
merupakan mekanisme pemutakhiran penghitungan referensi).std::unique_ptr
tidak memiliki jumlah referensi danstd::move
tidak ada hubungannya sama sekali dengan referensi (jadi "tidak" ada performa). Saya mengerti maksud Anda, sepertistd::shared_ptr
halnya memiliki jumlah referensi yang implikasi diperbarui olehstd::move
:)malloc
danfree
. Jadi ya, GC bisa jauh lebih cepat. (Perhatikan bahwa saya mengatakan "bisa" - tentu saja kinerja yang tepat dari setiap program dipengaruhi oleh banyak faktor.)Karena, ketika menggunakan kekuatan C ++, tidak perlu.
Herb Sutter: " Sudah bertahun-tahun saya tidak menghapus tulisan. "
lihat Menulis kode C ++ modern: bagaimana C ++ telah berkembang selama bertahun-tahun 21:10
Ini mungkin mengejutkan banyak programmer C ++ yang berpengalaman.
sumber
"Semua" seorang pemulung adalah proses yang berjalan secara berkala memeriksa untuk melihat apakah ada objek yang tidak direferensikan dalam memori dan jika ada menghapusnya. (Ya, saya tahu ini adalah penyederhanaan yang berlebihan). Ini bukan properti bahasa, tetapi kerangka kerja.
Ada pengumpul sampah yang ditulis untuk C dan C ++ - yang ini misalnya.
Salah satu alasan mengapa seseorang belum "ditambahkan" ke bahasa ini mungkin karena banyaknya volume kode yang ada yang tidak akan pernah menggunakannya karena mereka menggunakan kode mereka sendiri untuk mengelola memori. Alasan lain bisa jadi jenis aplikasi yang ditulis dalam C dan C ++ tidak perlu overhead yang terkait dengan proses pengumpulan sampah.
sumber
malloc
danfree
, Anda akan merusak program saya yang benar.free
sampai saya selesai dengan itu. Tapi pengumpul sampah yang Anda usulkan yang tidak membebaskan memori sampai saya secara eksplisit meneleponfree
bukan pengumpul sampah sama sekali.C dirancang di era ketika pengumpulan sampah nyaris bukan pilihan. Itu juga dimaksudkan untuk kegunaan di mana pengumpulan sampah umumnya tidak akan bekerja - logam kosong, lingkungan waktu nyata dengan memori minimal dan dukungan runtime minimal. Ingat bahwa C adalah bahasa implementasi untuk unix pertama, yang berjalan pada pdp-11 dengan 64 * K * byte memori. C ++ awalnya merupakan perluasan ke C - pilihan sudah dibuat, dan sangat sulit untuk mencangkokkan pengumpulan sampah ke bahasa yang ada. Ini adalah hal yang harus dibangun dari lantai dasar.
sumber
Saya tidak memiliki kutipan yang tepat, tetapi baik Bjarne dan Herb Sutter mengatakan sesuatu seperti itu:
Dalam C ++ modern Anda menggunakan pointer pintar dan karenanya tidak memiliki sampah.
sumber
Anda bertanya mengapa bahasa ini belum diperbarui untuk menyertakan pengumpul sampah opsional.
Masalah dengan pengumpulan sampah opsional adalah Anda tidak dapat mencampur kode yang menggunakan model yang berbeda. Artinya, jika saya menulis kode yang mengasumsikan Anda menggunakan pengumpul sampah, Anda tidak dapat menggunakannya dalam program Anda yang pengumpulan sampahnya dimatikan. Jika Anda melakukannya, itu akan bocor ke mana-mana.
sumber
Bisakah Anda bayangkan menulis penangan perangkat dalam bahasa dengan pengumpulan sampah? Berapa banyak bit yang bisa turun saat GC sedang berjalan?
Atau sistem operasi? Bagaimana Anda bisa memulai pengumpulan sampah yang berjalan bahkan sebelum Anda memulai kernel?
C dirancang untuk level rendah dekat dengan tugas perangkat keras. Masalah? apakah itu bahasa yang bagus sehingga merupakan pilihan yang baik untuk banyak tugas tingkat yang lebih tinggi juga. Bahasa tsar menyadari penggunaan ini tetapi mereka perlu mendukung persyaratan driver perangkat, kode tertanam dan sistem operasi sebagai prioritas.
sumber
Jawaban singkat dan membosankan untuk pertanyaan ini adalah bahwa perlu ada bahasa yang dikumpulkan non-sampah di luar sana untuk orang-orang yang menulis pengumpul sampah. Secara konseptual tidak mudah untuk memiliki bahasa yang pada saat yang sama memungkinkan untuk kontrol yang sangat tepat atas tata letak memori dan memiliki GC yang berjalan di atas.
Pertanyaan lainnya adalah mengapa C dan C ++ tidak memiliki pemulung. Yah, saya tahu C ++ memiliki beberapa dari mereka di sekitar tetapi mereka tidak benar-benar populer karena mereka dipaksa untuk berurusan dengan bahasa yang tidak dirancang untuk menjadi GC-ed di tempat pertama, dan orang-orang yang masih menggunakan C ++ di usia ini sebenarnya bukan jenis yang merindukan GC.
Selain itu, alih-alih menambahkan GC ke bahasa non-GC-ed lama, sebenarnya lebih mudah untuk membuat bahasa baru yang memiliki sebagian besar sintaksis yang sama sambil mendukung GC. Java dan C # adalah contoh yang bagus untuk ini.
sumber
Ada berbagai masalah, termasuk ...
delete
ataufree
secara eksplisit. Pendekatan GC masih memiliki keunggulan - tidak ada referensi yang menggantung - dan analisis statis dapat menangkap beberapa kasus, tetapi sekali lagi, tidak ada satu solusi sempurna untuk semua kasus.Pada dasarnya, sebagian adalah tentang usia bahasa, tetapi akan selalu ada tempat untuk bahasa non-GC - bahkan jika itu adalah sedikit tempat nichey. Dan serius, di C ++, kurangnya GC bukanlah masalah besar - memori Anda dikelola secara berbeda, tetapi tidak dikelola.
Microsoft mengelola C ++ memiliki setidaknya beberapa kemampuan untuk mencampur GC dan non-GC dalam aplikasi yang sama, memungkinkan campuran dan kecocokan dari keuntungan masing-masing, tetapi saya tidak memiliki pengalaman untuk mengatakan seberapa baik ini bekerja dalam prakteknya.
Tautan pelaporan ulang ke jawaban terkait saya ...
sumber
Pengumpulan sampah pada dasarnya tidak kompatibel dengan bahasa sistem yang digunakan untuk mengembangkan driver untuk perangkat keras yang mendukung DMA.
Sangat mungkin bahwa satu-satunya pointer ke objek akan disimpan dalam register perangkat keras di beberapa perangkat. Karena pengumpul sampah tidak akan tahu tentang ini, itu akan berpikir benda itu tidak dapat dijangkau dan mengumpulkannya.
Argumen ini berlaku ganda untuk memadatkan GC. Bahkan jika Anda berhati-hati untuk mempertahankan referensi dalam memori ke objek yang digunakan oleh periferal perangkat keras, ketika GC memindahkan objek, ia tidak akan tahu cara memperbarui pointer yang terdapat dalam register konfigurasi perifer.
Jadi sekarang Anda akan membutuhkan campuran buffer DMA bergerak dan objek yang dikelola GC, yang berarti Anda memiliki semua kelemahan dari keduanya.
sumber
Karena, C&C ++ adalah bahasa tingkat relatif rendah yang dimaksudkan untuk tujuan umum, bahkan, misalnya, untuk berjalan pada prosesor 16-bit dengan memori 1MB dalam sistem tertanam, yang tidak mampu membuang-buang memori dengan gc.
sumber
Ada pengumpul sampah di C ++ dan C. Tidak yakin bagaimana ini bekerja di C, tetapi di C ++ Anda dapat memanfaatkan RTTI untuk secara dinamis menemukan grafik objek Anda dan menggunakannya untuk pengumpulan sampah.
Sepengetahuan saya, Anda tidak dapat menulis Java tanpa pengumpul sampah. Pencarian kecil muncul ini .
Perbedaan utama antara Java dan C / C ++ adalah bahwa dalam C / C ++ pilihan selalu menjadi milik Anda, sedangkan di Jawa Anda sering dibiarkan tanpa opsi oleh desain.
sumber
Ini merupakan trade off antara kinerja dan keselamatan.
Tidak ada jaminan bahwa sampah Anda akan dikumpulkan di Jawa, jadi itu mungkin berkeliaran menghabiskan ruang untuk waktu yang lama, sementara pemindaian untuk objek yang tidak direferensikan (yaitu sampah) juga membutuhkan waktu lebih lama daripada secara eksplisit menghapus atau membebaskan objek yang tidak digunakan.
Keuntungannya, tentu saja, bahwa seseorang dapat membangun bahasa tanpa petunjuk atau tanpa kebocoran memori, sehingga seseorang lebih mungkin untuk menghasilkan kode yang benar.
Kadang-kadang ada sedikit 'religius' perdebatan ini - diperingatkan!
sumber
Berikut adalah daftar masalah inheren GC, yang membuatnya tidak dapat digunakan dalam bahasa sistem seperti C:
GC harus dijalankan di bawah level kode yang objeknya dikelola. Tidak ada level seperti itu di kernel.
GC harus menghentikan kode yang dikelola dari waktu ke waktu. Sekarang pikirkan apa yang akan terjadi jika hal itu terjadi pada kernel Anda. Semua pemrosesan pada mesin Anda akan berhenti selama, katakanlah, satu milidetik, sementara GC memindai semua alokasi memori yang ada. Ini akan mematikan semua upaya untuk menciptakan sistem yang beroperasi di bawah persyaratan waktu nyata yang ketat.
GC perlu dapat membedakan antara pointer dan non-pointer. Artinya, ia harus dapat melihat setiap objek memori yang ada, dan dapat menghasilkan daftar offset di mana pointer-nya dapat ditemukan.
Penemuan ini harus sempurna: GC harus mampu mengejar semua petunjuk yang ditemukannya. Jika dereferensi positif palsu, kemungkinan akan crash. Jika gagal menemukan negatif palsu, kemungkinan akan menghancurkan objek yang masih digunakan, menabrak kode yang dikelola atau merusak data secara diam-diam.
Ini benar-benar mengharuskan informasi jenis disimpan di setiap objek tunggal yang ada. Namun, baik C dan C ++ memungkinkan objek data lama polos yang tidak berisi informasi tipe.
GC adalah bisnis yang pada dasarnya lambat. Programmer yang telah disosialisasikan dengan Java mungkin tidak menyadari hal ini, tetapi program dapat menjadi urutan besarnya lebih cepat ketika mereka tidak diimplementasikan di Jawa. Dan salah satu faktor yang membuat Java lambat adalah GC. Inilah yang menghalangi bahasa-bahasa GCed seperti Java agar tidak digunakan dalam superkomputer. Jika mesin Anda menghabiskan satu juta tahun dalam konsumsi daya, Anda tidak ingin membayar bahkan 10% dari itu untuk pengumpulan sampah.
C dan C ++ adalah bahasa yang dibuat untuk mendukung semua kasus penggunaan yang mungkin. Dan, seperti yang Anda lihat, banyak dari kasus penggunaan ini dihambat oleh pengumpulan sampah. Jadi, untuk mendukung kasus penggunaan ini, C / C ++ tidak dapat dikumpulkan sebagai sampah.
sumber