Apakah Manajemen Sumber Daya Non-Deterministik Abstraksi Bocor?

9

Dari apa yang bisa saya lihat, ada dua bentuk pengelolaan sumber daya: perusakan deterministik dan eksplisit. Contoh dari yang pertama adalah destruktor C ++ dan pointer pintar atau sub DESTROY Perl, sementara contoh yang terakhir akan menjadi paradigma blok-untuk-mengelola-sumber daya Ruby atau antarmuka IDispose .NET.

Bahasa-bahasa yang lebih baru tampaknya memilih yang terakhir, mungkin sebagai efek samping dari penggunaan sistem pengumpulan sampah dari varietas non-referensi penghitungan.

Pertanyaan saya adalah ini: mengingat bahwa penghancur untuk penunjuk cerdas atau sistem pengumpulan sampah penghitungan referensi - hampir menjadi hal yang sama - memungkinkan perusakan sumber daya yang implisit dan transparan, apakah ini merupakan abstraksi yang kurang bocor daripada jenis non-deterministik yang mengandalkan eksplisit notasi?

Saya akan memberikan contoh nyata. Jika Anda memiliki tiga subclass C ++ dari superclass tunggal, satu mungkin memiliki implementasi yang tidak memerlukan penghancuran khusus. Mungkin itu melakukan sihirnya dengan cara lain. Fakta bahwa itu tidak memerlukan penghancuran khusus tidak relevan - semua subclass masih digunakan dengan cara yang sama.

Contoh lain menggunakan blok Ruby. Dua subclass perlu membebaskan sumber daya, sehingga superclass memilih antarmuka yang menggunakan blok di konstruktor, meskipun subclass spesifik lainnya mungkin tidak memerlukannya karena mereka tidak memerlukan penghancuran khusus.

Apakah ini kasus yang terakhir bocor detail implementasi perusakan sumber daya, sedangkan yang pertama tidak?

EDIT: Membandingkan, katakanlah, Ruby ke Perl mungkin lebih adil karena satu memiliki kehancuran deterministik dan yang lainnya belum, namun keduanya sama-sama mengumpulkan sampah.

Louis Jackman
sumber
5
Saya tergoda untuk mengatakan "ya", tetapi saya suka mendengar apa yang orang lain katakan tentang ini.
Bart van Ingen Schenau
Penghancuran sumber daya yang transparan? Terlepas dari kenyataan bahwa Anda harus menggunakan pointer pintar, bukan pointer normal? Saya tidak berpikir ini lebih transparan daripada hanya memiliki satu mekanisme (referensi) untuk mengakses objek (di C ++ Anda memiliki setidaknya empat atau lima).
Giorgio
@Iorgio: "Cara mengakses objek" cukup kabur. Apakah maksud Anda membaca atau menulis? Kualifikasi Const / Volatile? Pointer sebenarnya bukan "cara untuk mengakses objek"; cukup banyak ekspresi apa pun yang menghasilkan objek dan mendereferensi pointer tidak begitu istimewa.
MSalters
1
@Iorgio: Dalam arti OOP, Anda tidak dapat mengirim pesan ke pointer C ++. Anda perlu melakukan dereferensi pointer untuk mengirim pesan (*ptr).Message()atau yang setara ptr->Message(). Ada seperangkat ekspresi diizinkan yang tak terbatas, seperti ((*ptr))->Messagejuga yang setara. Tapi mereka semua bermuara padaexpressionIdentifyingAnObject.Message()
MSalters
1
Dengan penghitungan ulang, Anda harus berhati-hati menghindari lingkaran. Sehingga abstraksi itu bocor juga, hanya dengan cara yang berbeda.
CodesInChaos

Jawaban:

2

Teladan Anda sendiri menjawab pertanyaan itu. Penghancuran transparan jelas kurang bocor daripada penghancuran eksplisit. Itu bisa bocor, tapi kurang bocor.

Penghancuran eksplisit analog dengan malloc / gratis di C dengan semua jebakan. Mungkin dengan sedikit gula sintaksis untuk membuatnya tampak berdasarkan lingkup.

Beberapa manfaat dari penghancuran transparan lebih eksplisit: -
pola penggunaan yang sama - Anda
tidak bisa lupa untuk melepaskan sumber daya.
- detail bersihkan jangan membuang sampah lanskap pada titik penggunaan.

mike30
sumber
2

Kegagalan dalam abstraksi sebenarnya bukan fakta bahwa pengumpulan sampah adalah non-deterministik, melainkan pada gagasan bahwa objek "tertarik" pada hal-hal yang mereka pegang referensi, dan tidak tertarik pada hal-hal yang tidak mereka pegang. referensi. Untuk melihat alasannya, pertimbangkan skenario dari sebuah objek yang mempertahankan counter seberapa sering kontrol tertentu dicat. Pada saat pembuatan, ia berlangganan acara "cat" kontrol, dan saat pembuangannya berhenti berlangganan. Acara cukup klik increment lapangan, dan metode getTotalClicks()mengembalikan nilai bidang itu.

Ketika objek penghitung dibuat, itu harus menyebabkan referensi untuk dirinya sendiri disimpan dalam kontrol yang dimonitor. Kontrol benar-benar tidak peduli tentang objek penghitung, dan akan sama bahagia jika objek penghitung, dan referensi untuk itu, tidak ada lagi, tetapi selama referensi itu ada, ia akan memanggil pengendali acara objek itu setiap kali cat itu sendiri. Tindakan ini sama sekali tidak berguna bagi kontrol, tetapi akan bermanfaat bagi siapa saja yang akan memanggil getTotalClicks()objek tersebut.

Jika misalnya suatu metode adalah untuk membuat objek "penghitung cat" baru, melakukan beberapa tindakan pada kontrol, mengamati berapa kali kontrol dicat ulang, dan kemudian meninggalkan objek penghitung cat, objek akan tetap berlangganan acara bahkan meskipun tidak ada yang akan peduli jika objek dan semua referensi tentang itu menghilang begitu saja. Objek tidak akan memenuhi syarat untuk koleksi, namun, sampai kontrol itu sendiri. Jika metode itu adalah salah satu yang akan dipanggil ribuan kali dalam masa kendali [skenario yang masuk akal], itu dapat menyebabkan memori meluap tetapi untuk fakta bahwa biaya doa N kemungkinan akan menjadi O (N ^ 2) atau O (N ^ 3) kecuali pemrosesan berlangganan sangat efisien dan sebagian besar operasi sebenarnya tidak melibatkan pengecatan.

Skenario khusus ini dapat ditangani dengan memberikan kontrol menjaga referensi yang lemah ke objek counter daripada yang kuat. Model langganan yang lemah sangat membantu, tetapi tidak berfungsi dalam kasus umum. Misalkan alih-alih ingin memiliki objek yang memantau satu jenis peristiwa dari kontrol tunggal, seseorang ingin memiliki objek event-logger yang memantau beberapa kontrol, dan mekanisme penanganan peristiwa sistem sedemikian rupa sehingga setiap kontrol membutuhkan referensi ke objek event-logger yang berbeda. Dalam hal itu, objek yang menghubungkan kontrol ke event logger harus tetap hidup hanya selama keduanyakontrol dimonitor dan event logger tetap bermanfaat. Jika kontrol maupun pencatat peristiwa tidak memiliki referensi kuat ke acara penautan, itu tidak akan ada meskipun itu masih "berguna". Jika salah satu dari mereka memegang acara yang kuat, masa hidup dari objek yang terhubung mungkin akan sia-sia diperpanjang bahkan jika yang lain mati.

Jika tidak ada referensi ke objek di mana pun di alam semesta, objek tersebut dapat dengan aman dianggap tidak berguna dan dihilangkan dari keberadaan. Fakta bahwa referensi ada pada suatu objek, tidak berarti bahwa objek itu "berguna". Dalam banyak kasus, kegunaan sebenarnya dari objek akan tergantung pada keberadaan referensi ke objek lain yang - dari perspektif GC - sama sekali tidak terkait dengan mereka.

Jika objek secara deterministik diberitahu ketika tidak ada orang yang tertarik pada mereka, mereka akan dapat menggunakan informasi itu untuk memastikan bahwa siapa pun yang akan mendapat manfaat dari pengetahuan itu diberi informasi. Namun, dengan tidak adanya pemberitahuan semacam itu, tidak ada cara umum untuk menentukan objek apa yang dianggap "berguna" jika seseorang hanya mengetahui set referensi yang ada, dan bukan makna semantik yang melekat pada referensi tersebut. Dengan demikian, setiap model yang mengasumsikan bahwa keberadaan atau tidak adanya referensi cukup untuk manajemen sumber daya otomatis akan hancur bahkan jika GC dapat langsung mendeteksi pengabaian objek.

supercat
sumber
0

Tidak ada "destructor, atau antarmuka lain yang mengatakan" kelas ini harus dihancurkan "adalah kontrak dari antarmuka itu. Jika Anda membuat subtipe yang tidak memerlukan penghancuran khusus, saya akan cenderung menganggap bahwa pelanggaran terhadap Prinsip Substitusi Liskov .

Sedangkan untuk C ++ vs. yang lain, tidak ada banyak perbedaan. C ++ memaksa antarmuka itu pada semua objek itu. Abstraksi tidak bisa bocor ketika diminta oleh bahasa.

Telastyn
sumber
4
"Jika Anda membuat subtipe yang tidak memerlukan penghancuran khusus" Itu bukan pelanggaran LSP, karena larangan adalah kasus penghancuran khusus yang sah. Masalahnya adalah ketika Anda menambahkan persyaratan penghancuran ke kelas turunan.
CodesInChaos
Saya bingung di sini. Jika seseorang perlu menambahkan kode penghancuran khusus ke subclass C ++, itu tidak mengubah pola penggunaannya sama sekali, karena itu otomatis. Itu berarti superclass dan subclass masih dapat digunakan secara bergantian. Tetapi dengan notasi eksplisit untuk pengelolaan sumber daya, subkelas yang membutuhkan penghancuran eksplisit akan membuat penggunaannya tidak sesuai dengan superclass, bukan? (Dengan asumsi superclass tidak perlu kehancuran eksplisit.)
Louis Jackman
@ CodeInChaos - ah ya, saya kira itu benar.
Telastyn
@ ljackman: Kelas yang membutuhkan penghancuran khusus membebani siapa pun yang memanggil konstruktornya untuk memastikan bahwa itu dijalankan. Ini tidak membuat pelanggaran LSP karena DerivedFooThatRequiresSpecialDestructionhanya dapat dibuat oleh kode yang memanggil new DerivedFooThatRequiresSpecialDestruction(). Di sisi lain, metode pabrik yang mengembalikan DerivedFooThatRequiresSpecialDestructionkode ke yang tidak mengharapkan sesuatu yang memerlukan penghancuran, akan menjadi pelanggaran LSP.
supercat
0

Pertanyaan saya adalah ini: mengingat bahwa penghancur untuk penunjuk cerdas atau sistem pengumpulan sampah penghitungan referensi - hampir menjadi hal yang sama - memungkinkan perusakan sumber daya yang implisit dan transparan, apakah ini merupakan abstraksi yang kurang bocor daripada jenis non-deterministik yang mengandalkan eksplisit notasi?

Harus memperhatikan siklus dengan tangan bukanlah implisit atau transparan. Satu-satunya pengecualian adalah sistem penghitungan referensi dengan bahasa yang melarang siklus dengan desain. Erlang mungkin merupakan contoh dari sistem seperti itu.

Jadi keduanya mendekati kebocoran. Perbedaan utama adalah bahwa destruktor bocor di mana-mana di C ++ tetapi IDisposesangat jarang terjadi pada .NET.

Jon Harrop
sumber
1
Kecuali siklus sangat jarang dan praktis tidak pernah terjadi kecuali dalam struktur data yang secara eksplisit siklik. Perbedaan utama adalah bahwa destruktor di C ++ ditangani dengan benar di mana-mana tetapi IDispose jarang menangani masalah di .NET.
DeadMG
"Kecuali siklus sangat jarang". Dalam bahasa modern? Saya akan menantang hipotesis itu.
Jon Harrop