Kekurangan manajemen memori berbasis cakupan

38

Saya sangat suka manajemen memori berbasis ruang lingkup (SBMM), atau RAII , karena lebih umum (membingungkan?) Disebut oleh komunitas C ++. Sejauh yang saya tahu, kecuali untuk C ++ (dan C), tidak ada bahasa utama lain yang digunakan saat ini yang menjadikan SBMM / RAII mekanisme manajemen memori utama mereka, dan sebagai gantinya mereka lebih suka menggunakan pengumpulan sampah (GC).

Saya merasa ini agak membingungkan

  1. SBMM membuat program lebih deterministik (Anda bisa tahu persis kapan suatu objek dihancurkan);
  2. dalam bahasa yang menggunakan GC Anda sering harus melakukan manajemen sumber daya manual (lihat menutup file di Jawa, misalnya), yang sebagian mengalahkan tujuan GC dan juga rentan kesalahan;
  3. heap memory juga bisa (sangat elegan, imo) menjadi lingkup-terikat (lihat std::shared_ptrdi C ++).

Mengapa SBMM tidak lebih banyak digunakan? Apa kerugiannya?

Paul
sumber
1
Beberapa kelemahan (terutama menyangkut kecepatan) dibahas di Wikipedia: en.wikipedia.org/wiki/…
Philipp
2
Masalah manajemen sumber daya manual Java adalah efek samping dari tidak menjamin bahwa finalize()metode objek akan dipanggil sebelum pengumpulan sampah. Akibatnya, ini menciptakan kelas masalah yang sama yang seharusnya dipecahkan oleh pengumpulan sampah.
Blrfl
7
@ Blrfl Omong kosong. Manajemen sumber daya "manual" (untuk sumber daya selain memori, jelas) akan lebih disukai bahkan jika "masalah" itu tidak ada, karena GC dapat berjalan sangat lama setelah sumber daya menjadi tidak digunakan, atau bahkan tidak berjalan sama sekali. Itu tidak masalah untuk memori , dan manajemen memori adalah semua yang seharusnya dipecahkan oleh pengumpulan sampah.
4
btw. Saya suka menyebutnya sebagai SBRM karena Anda dapat menggunakan mekanisme yang sama untuk mengelola sumber daya secara umum, bukan hanya memori.
PlasmaHH

Jawaban:

27

Mari kita mulai dengan mendalilkan bahwa memori jauh (lusinan, ratusan atau bahkan ribuan waktu) lebih umum daripada semua sumber daya lainnya digabungkan. Setiap variabel tunggal, objek, anggota objek membutuhkan memori yang dialokasikan untuk itu dan dibebaskan nanti. Untuk setiap file yang Anda buka, Anda membuat lusinan hingga jutaan objek untuk menyimpan data yang ditarik dari file. Setiap aliran TCP berjalan bersama dengan sejumlah string byte sementara yang dibuat untuk ditulis ke aliran. Apakah kita ada di halaman yang sama di sini? Besar.

Agar RAII berfungsi (bahkan jika Anda memiliki smart pointer yang sudah jadi untuk setiap use case di bawah matahari), Anda perlu mendapatkan hak kepemilikan . Anda perlu menganalisis siapa yang harus memiliki objek ini atau itu, siapa yang tidak boleh, dan kapan kepemilikan harus ditransfer dari A ke B. Tentu, Anda bisa menggunakan kepemilikan bersama untuk semuanya , tetapi kemudian Anda akan meniru GC melalui pointer pintar. Pada saat itu menjadi lebih mudah dan lebih cepat untuk membangun GC ke dalam bahasa.

Pengumpulan sampah membebaskan Anda dari kekhawatiran ini akan sumber daya yang paling umum digunakan, memori. Tentu, Anda masih perlu membuat keputusan yang sama untuk sumber daya lain, tetapi itu jauh kurang umum (lihat di atas), dan kepemilikan yang rumit (misalnya dibagi) juga kurang umum. Beban mental berkurang secara signifikan.

Sekarang, Anda menyebutkan beberapa kerugian untuk membuat semua nilai sampah dikumpulkan. Namun, mengintegrasikan GC yang aman memori dan tipe nilai dengan RAII ke dalam satu bahasa sangat sulit, jadi mungkin lebih baik untuk memigrasikan trade off ini melalui cara lain?

Hilangnya determinisme ternyata tidak terlalu buruk dalam praktik, karena hanya mempengaruhi seumur hidup objek deterministik . Seperti dijelaskan dalam paragraf berikutnya, sebagian besar sumber daya (selain dari ingatan, yang berlimpah dan dapat didaur ulang dengan agak malas) tidak terikat dengan objek seumur hidup dalam bahasa-bahasa ini. Ada beberapa kasus penggunaan lainnya, tetapi jarang dalam pengalaman saya.

Poin kedua Anda, manajemen sumber daya manual, saat ini ditangani melalui pernyataan yang melakukan pembersihan berbasis ruang lingkup, tetapi tidak memasangkan pembersihan ini dengan waktu hidup objek (karenanya tidak berinteraksi dengan GC dan keamanan memori). Ini usingdalam C #, withdalam Python, try-dengan-sumber daya dalam versi Java terbaru.


sumber
1
Ini tidak menjelaskan mengapa model nondeterministic seperti GC harus lebih unggul daripada yang deterministik. usingpernyataan hanya dimungkinkan secara lokal. Tidak mungkin untuk membersihkan sumber daya yang disimpan dalam variabel anggota dengan cara itu.
Philipp
8
@Philipp Kepemilikan bersama untuk semuanya adalah GC, hanya yang sangat buruk. Jika Anda menggunakan "kepemilikan bersama" untuk menyiratkan penghitungan ulang, harap katakan demikian, tetapi simpan diskusi tentang siklus di komentar pada jawaban amon. Saya juga tidak yakin apakah penghitungan ref adalah deterministik dalam arti OP tertarik (objek dibebaskan sedini mungkin, siklus diskon, tetapi Anda sering tidak tahu kapan itu dengan melihat program). Selain itu, penghitungan referensi untuk semuanya lambat, jauh lebih lambat daripada GC penelusuran modern.
16
usingadalah lelucon dibandingkan dengan RAII, asal tahu saja.
DeadMG
3
@ Pilip tolong jelaskan metrik Anda untuk "atasan". Memang benar bahwa manajemen memori manual lebih cepat saat dijalankan untuk berurusan dengan manajemen memori. Namun biaya perangkat lunak tidak dapat dinilai murni pada waktu CPU yang dihabiskan untuk manajemen memori saja.
ArTs
2
@ Ark: Saya bahkan tidak perlu setuju dengan ini. RAII hadir dengan persyaratan bahwa objek harus dihancurkan karena meninggalkan ruang lingkup. Maka diperlukan loop untuk melakukan n kerusakan objek. Di bawah GC generasi modern, penghancuran itu dapat ditunda sampai akhir loop, atau bahkan lebih lambat, dan melakukan hanya satu operasi untuk menghancurkan ratusan iterasi senilai memori. GC bisa sangat sangat cepat dalam kasus yang baik.
Phoshi
14

RAII juga mengikuti dari manajemen memori penghitungan referensi otomatis, misalnya seperti yang digunakan oleh Perl. Walaupun penghitungan referensi mudah diterapkan, deterministik, dan cukup berkinerja, ia tidak dapat berurusan dengan referensi melingkar (menyebabkan kebocoran) yang mengapa itu tidak umum digunakan.

Bahasa yang dikumpulkan sampah tidak dapat menggunakan RAII secara langsung, tetapi sering menawarkan sintaksis dengan efek yang setara. Di Jawa, kami memiliki pernyataan coba-dengan-sumber daya

try (BufferedReader br = new BufferedReader(new FileReader(path))) { ... }

yang secara otomatis memanggil .close()sumber daya di blok keluar. C # memiliki IDisposableantarmuka, yang memungkinkan .Dispose()untuk dipanggil ketika meninggalkan using (...) { ... }pernyataan. Python memiliki withpernyataan:

with open(filename) as f:
    ...

yang bekerja dengan cara yang sama. Dalam putaran menarik tentang ini, metode buka file Ruby mendapat panggilan balik. Setelah panggilan balik dilakukan, file ditutup.

File.open(name, mode) do |f|
    ...
end

Saya pikir Node.js menggunakan strategi yang sama.

amon
sumber
4
Menggunakan fungsi urutan yang lebih tinggi untuk tanggal manajemen sumber daya jauh sebelum Ruby. Dalam Lisps, sangat umum untuk memiliki, katakanlah, with-open-filehandlefungsi yang membuka file, menghasilkannya ke fungsi dan setelah kembali fungsi tutup file lagi.
Jörg W Mittag
4
Argumen referensi siklik cukup umum, tetapi seberapa pentingkah sebenarnya? Referensi siklik dapat dikurangi menggunakan pointer lemah jika kepemilikan jelas.
Philipp
2
@ Pilip Ketika Anda menggunakan penghitungan referensi, kepemilikan biasanya tidak jelas. Juga, jawaban ini berbicara tentang bahasa yang menggunakan penghitungan ref secara eksklusif dan otomatis, sehingga referensi yang lemah tidak ada atau jauh lebih sulit digunakan daripada referensi yang kuat.
3
@Philipp cyclic struktur data sangat jarang, kecuali Anda bekerja dengan grafik yang rumit. Pointer lemah tidak membantu dalam grafik objek siklik umum, meskipun mereka membantu dalam kasus yang lebih umum seperti pointer orangtua di pohon. Solusi yang baik adalah menjaga objek konteks yang mewakili referensi ke seluruh grafik, dan mengelola kehancuran. Refcounting bukan pembuat kesepakatan, tetapi mengharuskan programmer sangat menyadari batasannya. Yaitu memiliki biaya kognitif sedikit lebih tinggi daripada GC.
amon
1
Alasan penting mengapa penghitungan ref jarang digunakan adalah bahwa penghitungan ref sering lebih lambat daripada GC, meskipun sederhana.
Rufflewind
14

Menurut pendapat saya, keuntungan pengumpulan sampah yang paling meyakinkan adalah memungkinkan kompabilitas . Kebenaran manajemen memori adalah properti lokal di lingkungan pengumpulan sampah. Anda dapat melihat setiap bagian secara terpisah dan menentukan apakah dapat membocorkan memori. Gabungkan sejumlah bagian yang benar dengan memori dan tetaplah benar.

Ketika Anda mengandalkan penghitungan referensi, Anda kehilangan properti itu. Apakah aplikasi Anda dapat membocorkan memori menjadi properti global seluruh aplikasi dengan penghitungan referensi. Setiap interaksi baru antar bagian memiliki kemungkinan untuk menggunakan kepemilikan yang salah dan merusak manajemen memori.

Ini memiliki efek yang sangat terlihat pada desain program dalam berbagai bahasa. Program dalam bahasa-bahasa GC cenderung menjadi sup yang lebih banyak dari objek-objek dengan banyak interaksi, sedangkan dalam bahasa-bahasa yang kurang GC seseorang cenderung lebih suka bagian-bagian yang terstruktur dengan interaksi yang terkontrol secara ketat dan terbatas di antara mereka.

Patrick
sumber
1
Kebenaran hanya dapat dikomposisikan jika objek hanya menyimpan referensi atas nama mereka sendiri, dan bukan atas nama target referensi tersebut. Begitu hal-hal seperti notifikasi masuk ke dalam campuran (Bob memegang referensi ke Joe karena Joe meminta Bob untuk memberi tahu dia ketika sesuatu terjadi dan Bob berjanji untuk melakukannya, tetapi Bob sebaliknya tidak peduli tentang Joe), kebenaran GC sering membutuhkan manajemen sumber daya yang mencakup semua bidang. [diimplementasikan secara manual dalam banyak kasus, karena sistem GC tidak memiliki otomatisasi C ++].
supercat
@supercat: "kebenaran GC sering membutuhkan manajemen sumber daya yang mencakup". Eh? Lingkup hanya ada dalam kode sumber dan GC hanya ada pada saat dijalankan (dan, oleh karena itu, sepenuhnya tidak menyadari keberadaan ruang lingkup).
Jon Harrop
@ JonHarrop: Saya menggunakan istilah "lingkup" dalam arti yang sama dengan C ++ "pointer lingkup" [umur objek harus dari wadah yang memegangnya], karena itulah penggunaan yang tersirat oleh pertanyaan asli. Maksud saya adalah bahwa objek membuat referensi yang berpotensi berumur panjang untuk diri mereka sendiri untuk tujuan seperti menerima acara mungkin tidak dapat dikomposisikan dalam sistem GC murni. Untuk kebenaran, referensi tertentu harus kuat dan referensi tertentu harus lemah, dan referensi mana yang perlu yang akan tergantung pada bagaimana suatu objek digunakan. Misalnya ...
supercat
... anggap objek Fred dan Barney mendaftar untuk pemberitahuan kapan pun sesuatu dalam direktori tertentu diubah. Pawang Fred tidak melakukan apa-apa selain menambah penghitung yang nilainya dapat dilaporkan berdasarkan permintaan, tetapi tidak memiliki kegunaan lain. Handler Barney akan memunculkan jendela baru jika file tertentu dimodifikasi. Demi kebenaran, Fred harus berlangganan dengan acara yang lemah, tetapi Barney harus kuat, tetapi objek pengatur waktu tidak memiliki cara untuk mengetahui hal itu.
supercat
@supercat: Benar. Saya tidak akan mengatakan itu terjadi "sering". Saya hanya menemukannya sekali dalam 30 tahun pemrograman.
Jon Harrop
7

Penutupan adalah fitur penting dari hampir semua bahasa modern. Mereka sangat mudah diimplementasikan dengan GC dan sangat sulit (meskipun bukan tidak mungkin) untuk mendapatkan yang benar dengan RAII, karena salah satu fitur utama mereka adalah mereka memungkinkan Anda untuk abstrak selama masa hidup variabel Anda!

C ++ hanya mendapatkan mereka 40 tahun setelah semua orang melakukannya, dan butuh banyak kerja keras oleh banyak orang pintar untuk memperbaikinya. Sebaliknya, banyak bahasa scripting yang dirancang dan diimplementasikan oleh orang-orang yang tidak memiliki pengetahuan dalam mendesain dan mengimplementasikan bahasa pemrograman memilikinya.

Jörg W Mittag
sumber
9
Saya tidak berpikir penutupan di C ++ adalah contoh yang baik. The lambdas di C ++ 11 hanyalah gula sintaksis untuk kelas functor (yang pra-tanggal C ++ 11 secara signifikan), dan sama-sama memori-tidak aman: Jika Anda menangkap sesuatu dengan referensi, dan memanggil penutupan setelah benda itu mati, Anda cukup mendapatkan UB, seperti memegang referensi lebih lama dari valid. Bahwa mereka muncul 40 tahun terlambat adalah karena pengakuan FP yang terlambat, bukan karena mencari tahu bagaimana membuat mereka aman. Dan ketika mendesainnya jelas merupakan tugas yang sangat besar, saya ragu bahwa sebagian besar upaya tersebut menjadi pertimbangan seumur hidup.
Saya setuju dengan delnan: C ++ tidak mendapatkan penutupan yang benar: Anda harus memprogramnya dengan sangat hati-hati jika Anda tidak ingin mendapatkan dump inti ketika Anda memintanya.
Giorgio
2
@delnan: catch-by-reference lambda's sangat sengaja memiliki [&]sintaks itu. Setiap programmer C ++ sudah mengaitkan &tanda dengan referensi dan tahu tentang referensi basi.
MSalters
2
@MSalters Apa maksud Anda? Saya telah menggambar koneksi referensi sendiri. Saya tidak mengatakan C ++ lambdas sangat tidak aman, saya mengatakan mereka persis tidak aman seperti referensi. Saya tidak berpendapat bahwa C + + lambdas buruk, saya menentang klaim jawaban ini (bahwa C + + mendapat penutupan sangat terlambat karena mereka harus mencari cara untuk melakukannya dengan benar).
5
  1. SBMM membuat program lebih deterministik (Anda bisa tahu persis kapan suatu objek dihancurkan);

Bagi sebagian besar programmer, OS bersifat non-deterministik, pengalokasi memori mereka adalah non-deterministik dan sebagian besar program yang mereka tulis bersamaan dan, karenanya, pada dasarnya non-deterministik. Menambahkan kendala bahwa destruktor disebut tepat di akhir lingkup daripada sedikit sebelum atau sedikit setelah itu bukan manfaat praktis yang signifikan bagi sebagian besar programmer.

  1. dalam bahasa yang menggunakan GC Anda sering harus melakukan manajemen sumber daya manual (lihat menutup file di Jawa, misalnya), yang sebagian mengalahkan tujuan GC dan juga rentan kesalahan;

Lihat usingdi C # dan usedi F #.

  1. heap memory juga dapat (sangat elegan, imo) terikat-lingkup (lihat std :: shared_ptr di C ++).

Dengan kata lain, Anda dapat mengambil tumpukan yang merupakan solusi tujuan umum dan mengubahnya menjadi hanya berfungsi dalam kasus tertentu yang sangat membatasi. Itu benar, tentu saja, tetapi tidak berguna.

Mengapa SBMM tidak lebih banyak digunakan? Apa kerugiannya?

SBMM membatasi apa yang dapat Anda lakukan:

  1. SBMM menciptakan masalah funarg ke atas dengan penutupan leksikal kelas satu yang mengapa penutupan populer dan mudah digunakan dalam bahasa seperti C # tetapi jarang dan rumit dalam C ++. Perhatikan bahwa ada kecenderungan umum terhadap penggunaan konstruksi fungsional dalam pemrograman.

  2. SBMM membutuhkan destruktor dan mereka menghalangi panggilan ekor dengan menambahkan lebih banyak pekerjaan yang harus dilakukan sebelum fungsi dapat kembali. Panggilan ekor berguna untuk mesin negara yang dapat diperpanjang dan disediakan oleh hal-hal seperti .NET.

  3. Beberapa struktur data dan algoritma terkenal sulit diimplementasikan menggunakan SBMM. Pada dasarnya di mana saja siklus itu terjadi secara alami. Terutama algoritma grafik. Anda secara efektif akhirnya menulis GC Anda sendiri.

  4. Pemrograman bersamaan lebih sulit karena aliran kontrol dan, oleh karena itu, umur objek secara inheren non-deterministik di sini. Solusi praktis dalam sistem penyampaian pesan cenderung berupa penyalinan pesan yang mendalam dan penggunaan masa hidup yang terlalu lama.

  5. SBMM membuat objek tetap hidup sampai akhir ruang lingkupnya dalam kode sumber yang seringkali lebih lama dari yang diperlukan dan bisa jauh lebih lama dari yang diperlukan. Ini meningkatkan jumlah sampah terapung (objek yang tidak terjangkau menunggu untuk didaur ulang). Sebaliknya, melacak pengumpulan sampah cenderung membebaskan benda segera setelah referensi terakhir kepada mereka menghilang yang bisa lebih cepat. Lihat mitos manajemen memori: ketepatan waktu .

SBMM sangat terbatas sehingga programmer membutuhkan jalan keluar untuk situasi di mana masa hidup tidak dapat dibuat untuk bersarang. Di C ++, shared_ptrmenawarkan rute pelarian tetapi bisa ~ 10x lebih lambat daripada melacak pengumpulan sampah . Jadi menggunakan SBMM dan bukan GC akan membuat kebanyakan orang salah menginjak sebagian besar waktu. Itu tidak berarti, bahwa itu tidak berguna. SBMM masih bernilai dalam konteks sistem dan pemrograman tertanam di mana sumber daya terbatas.

FWIW Anda mungkin ingin memeriksa Forth dan Ada, dan membaca karya Nicolas Wirth.

Jon Harrop
sumber
1
Jika Anda mengatakan bit mana yang saya bisa dapat menguraikan atau mengutip artikel.
Jon Harrop
2
Seberapa relevan 10x lebih lambat dalam beberapa kasus penggunaan yang jarang terjadi dibandingkan dengan yang ada di semua kasus penggunaan? C ++ memiliki unique_ptr dan untuk sebagian besar tujuan cukup. Di samping itu, daripada menyerang RAII melalui C ++ (bahasa yang banyak orang benci untuk menjadi bahasa kuno), jika Anda akan menyerang RAII melalui menyerang bahasa, coba saudara muda dari keluarga RAII, Rust misalnya. Karat pada dasarnya mendapatkan segalanya dengan benar bahwa C ++ salah sementara sebagian besar hal benar bahwa C ++ juga benar. Selanjutnya 'menggunakan' memberi Anda satu set kasus penggunaan yang sangat terbatas dan mengabaikan komposisi.
user1703394
2
"Seberapa relevan 10x lebih lambat dalam beberapa kasus penggunaan yang jarang dibandingkan dengan yang ada di semua kasus penggunaan?" Pertama, itu argumen melingkar: shared_ptrjarang ada di C ++ karena sangat lambat. Kedua, itu adalah perbandingan apel dan jeruk (seperti yang telah saya sebutkan di artikel yang saya sebutkan) karena shared_ptrberkali-kali lebih lambat daripada GC produksi. Ketiga, GC tidak ada di mana-mana dan dihindari dalam perangkat lunak seperti LMax dan mesin FIX Penambahan Cepat.
Jon Harrop
1
@ Jon Harrop, Jika Anda tidak tolong beri tahu saya. Apa resep ajaib yang telah Anda gunakan selama 30+ tahun untuk mengurangi efek transitif dari penggunaan sumber daya yang dalam? Tanpa resep ajaib seperti itu setelah 30+ tahun saya hanya bisa menyimpulkan bahwa Anda pasti salah paham karena digigit oleh penyebab lain.
user1703394
1
@ Jon Harrop, shared_ptr tidak jarang karena itu lambat, jarang karena alasan bahwa dalam sistem yang dirancang dengan baik kebutuhan untuk 'kepemilikan bersama' jarang terjadi.
user1703394
4

Melihat beberapa indeks popularitas seperti TIOBE (yang tentu saja dapat diperdebatkan, tapi saya kira untuk jenis pertanyaan Anda tidak masalah untuk menggunakan ini), Anda pertama kali melihat bahwa ~ 50% dari 20 teratas adalah "bahasa scripting" atau "dialek SQL" ", di mana" kemudahan penggunaan "dan sarana abstraksi memiliki jauh lebih penting daripada perilaku deterministik. Dari sisa bahasa "dikompilasi", ada sekitar 50% dari bahasa dengan SBMM dan ~ 50% tanpa. Jadi ketika mengeluarkan bahasa scripting dari perhitungan Anda, saya akan mengatakan anggapan Anda salah, di antara bahasa yang dikompilasi, yang dengan SBMM sama populernya dengan yang tidak.

Doc Brown
sumber
1
Bagaimana "kemudahan penggunaan" berbeda dari determinisme? Tidakkah bahasa deterministik dianggap lebih mudah digunakan daripada bahasa non-deterministik?
Philipp
2
@ Pilip Hanya hal yang menentukan atau tidak benar-benar penting. Obyek waktu hidup tidak masalah dengan sendirinya (meskipun C ++ dan teman-teman mengikat banyak hal yang penting untuk menolak waktu hidup, karena mereka dapat). Ketika objek yang tidak terjangkau dibebaskan, tidak masalah karena, menurut definisi, Anda tidak menggunakannya lagi.
Satu lagi, berbagai "bahasa scripting" seperti Perl dan Python juga menggunakan penghitungan referensi sebagai sarana utama untuk manajemen memori.
Philipp
1
@ Philipp Setidaknya di dunia Python, ini dianggap sebagai detail implementasi CPython, bukan properti bahasa (dan hampir setiap implementasi lainnya eschews refcounting). Selain itu, saya berpendapat bahwa penghitungan referensi catch-all-opt-out dengan siklus cadangan GC tidak memenuhi syarat sebagai SBMM atau RAII. Bahkan, Anda akan sulit sekali menemukan pendukung RAII yang menganggap gaya manajemen memori ini sebanding dengan RAII (sebagian besar karena tidak, siklus di mana saja dapat mencegah deallokasi yang cepat di tempat lain dalam program).
3

Satu keuntungan utama dari sistem GC yang belum pernah disebutkan siapa pun adalah bahwa referensi dalam sistem GC dijamin untuk mempertahankan identitasnya selama ada . Jika seseorang memanggil IDisposable.Dispose(.NET) atau AutoCloseable.Close(Java) pada suatu objek sementara salinan referensi ada, salinan itu akan terus merujuk ke objek yang sama. Objek tidak akan berguna untuk apa pun lagi, tetapi upaya untuk menggunakannya akan memiliki perilaku yang dapat diprediksi dikendalikan oleh objek itu sendiri. Sebaliknya, dalam C ++, jika kode memanggil deletesuatu objek dan kemudian mencoba menggunakannya, seluruh kondisi sistem menjadi benar-benar tidak terdefinisi.

Hal penting lain yang perlu diperhatikan adalah manajemen memori berbasis lingkup bekerja sangat baik untuk objek dengan kepemilikan yang jelas. Ini bekerja kurang baik, dan kadang-kadang benar-benar buruk, dengan benda-benda yang tidak memiliki kepemilikan yang jelas. Secara umum, objek yang dapat berubah harus memiliki pemilik, sedangkan objek yang tidak dapat diubah tidak perlu, tetapi ada kerutan: sangat umum untuk kode menggunakan instance dari tipe yang bisa berubah untuk menyimpan data yang tidak dapat diubah, dengan memastikan bahwa tidak ada referensi yang akan terpapar ke kode yang mungkin mengubah contoh. Dalam skenario seperti itu, instance dari kelas yang dapat berubah mungkin dibagi di antara beberapa objek yang tidak dapat diubah, dan dengan demikian tidak memiliki kepemilikan yang jelas.

supercat
sumber
4
Properti yang Anda rujuk di babak pertama adalah keamanan memori. Sementara GC adalah cara yang sangat mudah untuk mencapai keamanan memori, GC tidak diperlukan: Lihatlah Rust untuk contoh yang dilakukan dengan baik.
@delnan: Ketika saya melihat rust-lang.org, browser saya sepertinya tidak bisa bernavigasi dari sana; di mana saya harus mencari informasi lebih lanjut? Kesan saya adalah bahwa keamanan memori tanpa GC memberlakukan batasan tertentu pada struktur data yang mungkin tidak cocok dengan semua hal yang mungkin perlu dilakukan aplikasi, tetapi saya akan dengan senang hati terbukti salah.
supercat
1
Saya tidak tahu satu (atau bahkan set kecil) referensi yang baik untuk ini; pengetahuan Rust saya telah terakumulasi selama satu atau dua tahun membaca milis (dan semua hal yang terkait dengan surat, termasuk berbagai tutorial, posting blog desain bahasa, masalah github, ThisWeekInRust, dan banyak lagi). Untuk mengatasi secara singkat kesan Anda: Ya, setiap konstruk aman (harus) memberlakukan batasan, tetapi untuk hampir semua bagian kode yang aman memori, konstruk aman yang sesuai ada atau dapat ditulis. Sejauh ini yang paling umum sudah ada dalam bahasa dan stdlib, semua yang lain dapat ditulis dalam kode pengguna.
@delnan: Apakah Rust memerlukan pembaruan yang saling bertautan ke penghitung referensi, atau apakah ia memiliki cara lain untuk berurusan dengan objek yang tidak dapat diubah (atau objek yang dapat dibungkus yang tidak dapat diubah) yang tidak memiliki kepemilikan yang pasti? Apakah Rust memiliki konsep petunjuk "memiliki-benda" dan "tidak memiliki"? Saya ingat sebuah makalah tentang petunjuk "Xonor" yang membahas gagasan objek yang memiliki referensi tunggal yang "memiliki" mereka, dan referensi lain yang tidak; ketika referensi "memiliki" keluar dari ruang lingkup, semua referensi yang tidak memiliki akan menjadi referensi ke benda mati, dan akan dapat diidentifikasi sebagai ...
supercat
1
Saya tidak berpikir komentar Stack Exchange adalah media yang tepat untuk melakukan tur melalui bahasa. Jika Anda masih tertarik, Anda dapat langsung ke sumbernya (#rust IRC, mailing list karat-dev, dll.) Dan / atau hubungi saya di ruang obrolan (Anda harus dapat membuatnya).
-2

Pertama, sangat penting untuk menyadari bahwa menyamakan RAII dengan SBMM. atau bahkan ke SBRM. Salah satu kualitas RAII yang paling esensial (dan paling tidak diketahui atau paling tidak dihargai) adalah kenyataan bahwa RAII menjadikan 'menjadi sumber daya' properti yang TIDAK transitif terhadap komposisi.

Posting blog berikut membahas aspek penting RAII ini dan membandingkannya dengan perubahan sumber daya dalam bahasa GCed yang menggunakan GC non-deterministik.

http://minorfs.wordpress.com/2011/04/29/why-garbage-collection-is-anti-productive/

Penting untuk dicatat bahwa sementara RAII sebagian besar digunakan dalam C ++, Python (akhirnya versi non-VM) memiliki destruktor dan deterministik GC yang memungkinkan RAII untuk digunakan bersama dengan GC. Terbaik dari kedua dunia itu.

pengguna1703394
sumber
1
-1 Itu salah satu artikel terburuk yang pernah saya baca.
Jon Harrop
1
Masalah dalam bahasa bukan karena mereka mendukung GC, tetapi mereka meninggalkan RAII. Tidak ada alasan bahasa / kerangka kerja seharusnya tidak dapat mendukung keduanya.
supercat
1
@ Jon Harrop, bisakah Anda menguraikan. Dari klaim yang dibuat dalam artikel, apakah ada satu dari 3 klaim pertama yang tidak berlaku? Saya pikir Anda mungkin tidak setuju dengan klaim produktivitas, tetapi 3 klaim lainnya benar-benar valid. Yang paling penting yang pertama tentang transitivitas menjadi sumber daya.
user1703394
2
@ user1703394: Pertama, seluruh artikel ini didasarkan pada "bahasa GC" strawman ketika, pada kenyataannya, tidak ada hubungannya dengan pengumpulan sampah. Kedua, ia menyalahkan pengumpulan sampah ketika, pada kenyataannya, kesalahan terletak pada pemrograman berorientasi objek. Akhirnya, argumennya terlambat 10 tahun. Sebagian besar programmer sudah dimodernisasi ke bahasa sampah dikumpulkan justru karena mereka menawarkan produktivitas yang jauh lebih tinggi.
Jon Harrop
1
Contoh konkretnya (RAM, pegangan file terbuka, kunci, utas) cukup jelas. Saya sulit sekali mengingat kapan terakhir kali saya harus menulis kode yang berhubungan langsung dengan semua itu. Dengan RAM, GC mengotomatiskan segalanya. Dengan pegangan file saya menulis kode seperti di File.ReadLines file |> Seq.lengthmana abstraksi menangani penutupan untuk saya. Kunci dan utas saya telah diganti dengan .NET Taskdan F # MailboxProcessor. Seluruh ini "Kami meledak jumlah manajemen sumber daya manual" hanya omong kosong.
Jon Harrop