Neden çöp toplanan dillerdeki yıkıcı paradigması yaygın olarak bulunmuyor?


27

Çöp toplama dil tasarımı ile ilgili kararlar hakkında fikir edinmek. Belki bir dil uzmanı beni aydınlatabilir? C ++ arkaplanından geliyorum, bu yüzden bu alan benim için şaşırtıcı.

Neredeyse bütün modern çöpler, Ruby, Javascript / ES6 / ES7, Actionscript, Lua, vb. Gibi OOPy nesne desteğiyle dilleri topladı. Python class __del__()yöntemi ile tek gibi görünüyor . Bu neden? Nesneler üzerinde yıkıcı / sonlandırma yönteminin etkili bir şekilde uygulanmasını önleyen otomatik çöp toplama sistemli diller içinde işlevsel / teorik sınırlamalar var mı?

Bu dillerin hafızayı yönetmeye değer tek kaynak olarak gördüğünü çok az buluyorum . Peki ya yuvalar, dosya tutamaçları, uygulama durumları? Bellek dışı kaynakları temizlemek için özel bir mantık uygulama yeteneği ve nesne sonlandırmasına ilişkin durumlar olmadan, uygulamamı özel myObject.destroy()stil çağrılarıyla karıştırmam, temizleme mantığını "sınıfımın" dışına yerleştirmem, kapsüllendirme girişimi kırmam ve benim kimliğimi yeniden düzenlemem gerekiyor gc tarafından otomatik olarak ele alınmak yerine insan hatası nedeniyle kaynak sızıntılarına uygulama.

Nesne imhasında özel bir mantık yürütme imkânı bu dillere götüren dil tasarım kararları nelerdir? İyi bir sebep olduğunu hayal etmeliyim. Nesnelerin imhasını / sonlandırılmasını desteklemeyen bu dillerde sonuçlanan teknik ve teorik kararları daha iyi anlamak istiyorum.

Güncelleştirme:

Belki de sorumu ifade etmenin daha iyi bir yolu:

Neden bir dil, özel örnekleme (yapıcılar) ile birlikte sınıf veya sınıf benzeri yapılara sahip yerleşik nesne örnekleri kavramına sahip olsa da, imha etme / sonlandırma işlevselliğini tamamen göz ardı etsin? Otomatik çöp toplama hizmeti sunan diller, bir nesnenin artık kullanılmadığı durumlarda% 100 kesinlikte olduğu gibi, nesnelerin imha edilmesini / sonlandırılmasını destekleyen ana adaylar gibi görünmektedir. Ancak bu dillerin çoğu onu desteklemiyor.

Yıkıcıyı asla arayamayacak bir durum olduğunu sanmıyorum, çünkü bu, gcs'yi önlemek için tasarlanan çekirdek bellek sızıntısı olacak. Yıkıcı / sonlandırıcının gelecekte belirsiz bir süreye kadar çağrılmayacağı, ancak Java veya Python'un işlevselliği desteklemesini durduramayacağına dair olası bir argüman görebiliyordum.

Herhangi bir nesne sonlandırma biçimini desteklememek için ana dil tasarım nedenleri nelerdir?


9
Belki de yalan finalize/ destroyyalan mı? Yürütüleceğine dair hiçbir garanti yok. Ve bile, ne zaman (otomatik çöp toplama verildiğinde) ve gerekli bağlam hala orada (zaten toplanmış olabilir) bilmiyorsunuz. Dolayısıyla, tutarlı bir durumu başka yollarla sağlamak daha güvenlidir ve kişi programlayıcıyı buna zorlamak isteyebilir.
Raphael

1
Bence bu soru sınırda görünmüyor. Eğlendirmek istediğimiz türden bir programlama dili tasarım sorusu mu, yoksa programlamaya daha yönelik bir site için mi? Topluluk oyları lütfen.
Raphael

14
PL tasarımında güzel bir soru, hadi alalım.
Andrej Bauer

3
Bu gerçekten statik / dinamik bir ayrım değil. Çoğu statik dilde sonlandırıcı yoktur. Aslında, azınlıkta kesinleştirici olan diller değil mi?
Andrej Bauer

1
Burada bir soru olduğunu düşünün ... terimleri biraz daha fazla tanımlasanız daha iyi olur java sonunda nesnelerin imhasına bağlı olmayan ancak yöntemden çıkan bir blok var. Kaynaklarla baş etmenin başka yolları da var. örneğin, java'da, bir bağlantı havuzu zaman içinde kullanılmayan [x] giden ve onları geri alan bağlantılarla ilgilenebilir. zarif değil ama işe yarıyor. Sorunuzun cevabının bir kısmı çöp toplama kabiliyetinin kabaca özgün değil, anlık bir süreç olmadığı ve artık kullanılmayan nesneler tarafından değil, bellek kısıtlamaları / tavanları tarafından tetiklendiğidir.
Ocak'ta 15

Yanıtlar:


10

Bahsettiğiniz kalıp, nesnelerin kaynaklarını nasıl temizleyeceklerini bildikleri, ilgili üç kategoriye ayrılır. Hadi conflate değil yıkıcılar ile finalizers - tek çöp toplama ile ilgilidir:

  • Sonlandırıcıyı model : temizleme yöntemi otomatik olarak adlandırılan, Programcı tarafından tanımlanan, otomatik olarak açıklamıştır.

    Kesiciler, bir çöp toplayıcı tarafından dağıtılmadan önce otomatik olarak çağrılır. Kullanılan çöp toplama algoritması nesne yaşam çevrimlerini belirleyebiliyorsa bu terim geçerlidir.

  • Yıkıcı desen : temizleme yöntemi otomatik sadece bazen denilen, programcı tarafından tanımlanan, otomatik ilan etti.

    Yığın atanan nesneler için yıkıcılar otomatik olarak çağrılabilir (çünkü nesne ömrü belirleyicidir), ancak yığın tarafından ayrılan nesneler için olası tüm yürütme yollarında açık bir şekilde çağrılmalıdır (çünkü nesne ömrü belirleyici değildir).

  • Tek kullanımlık model : temizleme yöntemi, programcı tarafından bildirilmiş, tanımlanmış ve çağrılmıştır.

    Programcılar bir elden çıkarma yöntemi yapar ve kendileri olarak adlandırır - özel myObject.destroy()yönteminizin düşeceği yer burasıdır . İmha etme kesinlikle gerekliyse, imha ediciler tüm olası uygulama yollarında çağrılmalıdır.

Sonlandırıcılar, aradığınız droidlerdir.

Sonlandırıcı kalıbı (sorunuzun sorduğu kalıp), bir çöp toplayıcı tarafından karşılıklı ıslah için nesneleri sistem kaynaklarıyla (soketler, dosya tanımlayıcıları vb.) İlişkilendirme mekanizmasıdır. Ancak, sonlandırıcılar, temel olarak kullanımdaki çöp toplama algoritmasının insafına kalmaktadır.

Bu varsayımınızı düşünün:

Otomatik çöp toplama sunan diller ... bir nesne artık kullanılmadığında% 100 kesinlikte bilir.

Teknik olarak yanlış (teşekkürler, @babou). Çöp toplama temelde nesnelerle değil hafıza ile ilgilidir. Bir toplama algoritması bir nesnenin hafızasının artık kullanılmadığının farkına varırsa ya da algoritma ve (muhtemelen) nesnelerinizin birbirlerini ifade etmelerine bağlıdır. İki tip çalışma zamanı toplayıcı hakkında konuşalım. Bunları temel tekniklerle değiştirmek ve geliştirmek için birçok yol vardır:

  1. GC'nin izlenmesi. Bunlar hafıza değil, nesneler değil. Bunu yapmadıkça, bellekteki nesnelere yapılan referansları geri alamazlar. Arttırılmadığı sürece, bu GC'ler, hafızasına erişilemediğini bilseler bile, bir nesnenin ne zaman sonlandırılabileceğini bilemez. Bu nedenle, sonlandırıcı çağrıları garanti edilmez.

  2. Referans Sayma GC . Bunlar hafızayı izlemek için nesneler kullanır. Yönlendirilmiş bir referans grafiği ile nesne ulaşılabilirliğini modelliyorlar. Nesne referans grafiğinizde bir döngü varsa, o zaman döngüdeki tüm nesneler kesinlikle sonlandırıcılarına sahip olmaz (açıkça program sonlandırmasına kadar). Yine, sonlandırıcı çağrıları garanti edilmez.

TLDR

Çöp toplama zor ve çeşitlidir. Bir sonlandırıcı çağrısı program sonlandırılmadan önce garanti edilemez.


Bunun statik v. Dinamik olmadığı doğru. Çöp toplayan dillerle ilgili bir sorun. Çöp toplama karmaşık bir sorundur ve muhtemelen göz önünde bulundurulması gereken çok sayıda vaka olması nedeniyle birincil nedendir (örneğin, eğer mantık finalize(), nesnenin yeniden başvurulanmasına neden oluyorsa neden olur?). Ancak, sonlandırıcının program sonlandırılmadan önce çağrılmasını garanti edememek, Java'nın bunu desteklemesini engellemedi. Cevabınızın yanlış olduğunu söylememek, belki de eksik. Hala çok iyi bir yazı. Teşekkür ederim.
dbcb

Geri dönüşünüz için teşekkür ederiz. İşte cevabımı tamamlama girişimi: Kesinleştiricileri açıkça ihmal ederek bir dil kullanıcılarını kendi kaynaklarını yönetmeye zorluyor. Birçok sorun türü için bu muhtemelen bir dezavantajdır. Ben finalizers gücüne sahip, çünkü Şahsen ben, Java'nın seçim tercih ve yazma ve kendi Öğütücüyü kullanmamı durdurma şey yok. Java diyor ki, "Hey, programcı. Sen bir aptal değilsin, işte bir finaliser. Sadece dikkatli ol."
kdbanman

1
Asıl sorumu, bunun çöp toplayan dillerle ilgilendiğini yansıtacak şekilde güncelledim. Cevabınızı kabul ediyorum. cevaplamak için zaman ayırdığınız için teşekkür ederiz.
dbcb

Memnuniyetle yardım ettim. Yorum açıklamam cevabımı daha mı netleştirdi?
kdbanman

2
Bu iyi. Bana göre, buradaki asıl cevap, dillerin bunu uygulamamayı seçmesidir, çünkü algılanan değer, işlevselliği uygulama sorunlarından ağır basmaz. İmkansız değil (Java ve Python'un gösterdiği gibi), ancak birçok dilin yapmamayı tercih ettiği bir takas var.
dbcb

5

Kısaca

Sonlandırma, çöp toplayıcıların yapması gereken basit bir mesele değildir. Referans sayma GC ile kullanmak kolaydır, ancak bu GC ailesi genellikle eksiktir; bellek sızıntılarının, bazı nesnelerin ve yapıların açık bir şekilde imha edilmesi ve sonlandırılmasıyla telafi edilmesini gerektirir. Çöp toplayıcılarının izlenmesi çok daha etkilidir, ancak kullanılmayan hafızayı tanımlamak yerine, sonuç ve imha edilmek istenen nesneyi tanımlamayı çok daha zorlaştırır, sadece kullanılmayan hafızayı tanımlamak yerine, böylece daha karmaşık bir yönetim gerektiren, zaman ve mekanda ve karmaşıklığı gerektiren hayata geçirme.

Giriş

İstediğiniz şeyin çöp toplama dillerinin neden açıklamada belirtildiği gibi çöp toplama işlemi içindeki otomatik olarak imha / sonlandırma işlemlerini gerçekleştirmediğini farz ediyorum:

Bu dillerin hafızayı yönetmeye değer tek kaynak olarak gördüğünü çok az buluyorum. Peki ya yuvalar, dosya tutamaçları, uygulama durumları?

Kdbanman tarafından verilen kabul edilen cevaba katılmıyorum . Belirtilen gerçekler çoğunlukla doğru olmakla birlikte, referans sayımına yönelik kuvvetli bir önyargı olmasına rağmen, soruda şikayet edilen durumu doğru şekilde açıkladıklarına inanmıyorum.

Bu cevapta geliştirilen terminolojinin bir sorun olduğuna inanmıyorum ve işleri karıştırması daha olası. Aslında, sunulduğu gibi, terminoloji çoğunlukla, yaptıklarından ziyade prosedürlerin aktive edilmesiyle belirlenir. Mesele şu ki, her durumda, bir miktar temizleme işlemi ile artık ihtiyaç duyulmayan bir nesneyi sonlandırmaya ve kullandığı kaynakları serbest bırakmaya, hafızanın sadece bir tanesi olmasına ihtiyaç duyulması gerekiyor. İdeal olarak, nesnelerin artık bir çöp toplayıcısı aracılığıyla kullanılmayacak olması durumunda, bunların tümü otomatik olarak yapılmalıdır. Uygulamada, GC eksik veya eksik olabilir ve bu sonuçlandırma ve geri kazanma programı tarafından açıkça tetiklenmesi ile telafi edilir.

Program tarafından açık bir şekilde tetikleme bir problemdir, çünkü halen kullanılmakta olan bir nesne açıkça sonlandırıldığında programlama hatalarının analizini zorlaştırabilir.

Bu nedenle, kaynakları geri kazanmak için otomatik çöp toplamaya güvenmek çok daha iyidir. Ancak iki sorun var:

  • Bazı çöp toplama tekniği, kaynakların tamamen geri kazanılmasını önleyen bellek sızıntılarına izin verecektir. Bu, referans sayımı GC için iyi bilinmektedir, ancak bazı veri organizasyonlarını dikkatsizce kullanırken diğer GC teknikleri için görünebilir (burada tartışılmayan nokta).

  • GC tekniği artık kullanılmayan bellek kaynaklarını tanımlamakta iyi olabilirken, içerdiği nesneleri sonlandırmak basit olmayabilir ve bu genellikle bu nesneler tarafından kullanılan ve genellikle sonlandırmanın amacı olan diğer kaynakları geri alma sorununu zorlaştırır.

Son olarak, sıklıkla unutulan önemli bir nokta, GC kancalarının uygun kancalar sağlanmışsa ve bir GC döngüsünün maliyetinin buna değdiği takdirde, sadece bellek sıkıntısı değil, herhangi bir şey tarafından tetiklenebilmesidir. Bu nedenle, bazılarını serbest bırakma umuduyla, herhangi bir kaynak eksik olduğunda bir GC başlatmak iyidir.

Referans sayma çöp toplayıcıları

Referans sayımı, döngüleri doğru şekilde işlemeyecek zayıf bir çöp toplama tekniğidir . Eski yapıları tahrip etmek ve diğer kaynakları geri almak, sadece hafızayı geri kazanmakta zayıf olduğu için gerçekten zayıf olurdu. Ancak sonlandırıcılar, bir referans sayma çöp toplayıcısı (GC) ile en kolay şekilde kullanılabilir, çünkü bir ref-sayma GC, ref sayısı 0'a düştüğünde bir yapıyı geri alır, bu sırada adresi, türü ile birlikte ya da statik olarak bilinir. veya dinamik olarak. Bu nedenle, uygun sonlandırıcıyı uyguladıktan sonra belleği tam olarak geri kazanmak ve işlemi tüm sivri uçlu nesnelerde (muhtemelen sonlandırma prosedürü yoluyla) tekrar tekrar çağırmak mümkündür.

Kısaca, sonuçlandırma Ref Sayma GC ile gerçekleştirilmesi kolaydır, ancak aslında dairesel yapılardan ötürü GC'nin "eksikliğinden" ziyade, hafıza kazanımının tam olarak aynı derecede olması gerekir. Başka bir deyişle, referans sayımı ile bellek, yuvalar, dosya tanıtıcıları, vb. Gibi diğer kaynaklar kadar zayıf bir şekilde yönetilir .

Gerçekten de, Ref Say GC , döngüsel yapıların geri kazanılmasındaki yetersizlik (genel olarak) bellek sızıntısı olarak görülebilir . Tüm GC’lerin bellek sızıntılarından kaçınmasını bekleyemezsiniz. GC algoritmasına ve dinamik olarak mevcut olan tipte yapı bilgisine (örneğin muhafazakar GC'de ) bağlıdır.

Çöp toplayıcılarının izlenmesi

Bu tür sızıntılar olmadan daha güçlü GC ailesi , iyi tanımlanmış kök işaretçilerinden başlayarak hafızanın canlı kısımlarını araştıran izleme ailesidir . Hafızanın bu izleme sürecinde ziyaret edilmeyen tüm kısımları (çeşitli şekillerde ayrı ayrı ayrıştırılabilir, ancak basitleştirmek zorundayım), hafızanın kullanılmayan kısımlarıdır, bu nedenle geri kazanılabilir 1 . Bu koleksiyonerler, ne olursa olsun, program tarafından erişilemeyen tüm hafıza parçalarını geri alacaktır. Dairesel yapıları geri kazanıyor ve daha gelişmiş GC bu paradigmanın bazı varyasyonlarına dayanıyor, bazen oldukça karmaşık. Bazı durumlarda referans sayımı ile birleştirilebilir ve zayıf yönlerini telafi edebilir.

Bir sorun, ifadenizin ( sorunun sonunda) olmasıdır:

Otomatik çöp toplama hizmeti sunan diller, bir nesnenin artık kullanılmadığı durumlarda% 100 kesinlikte olduğu gibi, nesnelerin imha edilmesini / sonlandırılmasını destekleyen ana adaylar gibi görünmektedir.

koleksiyoncuları izlemek için teknik olarak yanlıştır .

% 100 kesin olarak bilinen şey, hafızanın hangi kısımlarının artık kullanılmadığıdır . (Daha doğrusu, artık erişilebilir olmadıkları söylenmelidir , çünkü program mantığına göre artık kullanılamayan bazı kısımlar, programda hala işe yaramaz bir işaretçi varsa, kullanımda olduğu düşünülmektedir. veri.) Ancak , hafızanın bu kullanılmamış kısımlarında hangi kullanılmamış nesnelerin saklanmış olabileceğini bilmek için daha fazla işlem ve uygun yapılara ihtiyaç vardır . Program hafızanın bu kısımlarına bağlı olmadığı için program tarafından bilinenlerden tespit edilemez.

Böylece bir çöp toplama işleminden sonra, artık kullanılmayan nesneleri içeren bellek parçaları kalır, ancak bu nesnelerin doğru sonlandırmayı uygulamak için ne olduklarını bilmenin bir yolu yoktur. Ayrıca, eğer izleyici toplayıcı işaretleme ve tarama tipi ise, parçaların bir kısmı daha önce GC geçişinde sonlandırılmış, ancak parçalanma nedenlerinden dolayı kullanılmamış nesneler içerebilir. Ancak bu, genişletilmiş açık tipli yazım kullanarak ele alınabilir.

Basit bir koleksiyoncu yalnızca bu bellek parçalarını geri alırken, daha fazla uzatmadan, sonlandırma kullanılmayan belleği keşfetmek, içerdiği nesneleri tanımlamak ve sonlandırma prosedürlerini uygulamak için belirli bir geçişi gerektirir. Ancak böyle bir keşif, orada saklanan nesnelerin türünün belirlenmesini gerektirir ve varsa uygun sonlandırmayı uygulamak için de tür belirlenmesi gerekir.

Bu, GC zamanındaki ekstra masrafları (ekstra geçiş) ve bu teknikte çeşitli tekniklerle uygun tip bilgiyi elde etmek için muhtemelen ekstra bellek maliyetlerini gerektirir. Bu maliyetler, çoğu zaman yalnızca birkaç nesneyi sonuçlandırmak isteyeceği için önemli olabilir, buna ek olarak zaman ve mekan genelindeki tüm nesneler ile ilgili olabilir.

Başka bir nokta, zaman ve mekan ek yükünün sadece GC yürütme ile değil, program kod yürütme ile ilgili olabileceğidir.

Belirli konulara işaret ederek daha kesin bir cevap veremem, çünkü listelediğiniz dillerin çoğunun özelliklerini bilmiyorum. C durumunda, yazmak, muhafazakar koleksiyoncuların gelişmesine yol açan çok zor bir konudur. Tahminim bunun C ++ 'ı da etkileyeceği, ancak C ++ konusunda uzman değilim. Bu muhafazakar GC hakkındaki araştırmaların çoğunu yapan Hans Boehm tarafından onaylanmış görünüyor . Muhafazakar GC, sistematik olarak kullanılmayan tüm bellekleri tam olarak geri alamaz, çünkü veriler üzerinde kesin tip bilgisi olmayabilir. Aynı nedenle, kesinleştirme prosedürlerini sistematik olarak uygulayamazdı.

Yani, bazı dillerden bildiğiniz gibi, istediğinizi yapmak mümkündür. Ama bedava gelmiyor. Dile ve uygulanmasına bağlı olarak, bu özelliği kullanmadığınızda bile bir maliyet getirebilir. Bu sorunları ele almak için çeşitli teknikler ve takaslar düşünülebilir, ancak bu makul bir cevap kapsamı dışındadır.

1 - Bu, toplama koleksiyonunun (hem kopya hem de işaretleme ve tarama işlemlerini içeren) soyut bir sunumudur, işler, izleme toplayıcısının türüne göre değişir ve hafızanın kullanılmayan kısmını araştırmak, kopyalamanın veya işaretlemenin ve süpürme kullanılır.


Çöp toplama konusunda çok fazla detay veriyorsunuz. Bununla birlikte, cevabınız aslında benimkine uymuyor - özeti ve TLDR'm aynı şeyi söylüyor. Ve buna değer ne olursa olsun, cevabım referans olarak GC'yi "güçlü bir önyargı" olarak değil, örnek olarak kullanır.
kdbanman

Daha iyice okuduktan sonra anlaşmazlığı görüyorum. Buna göre düzenleyeceğim. Ayrıca, terminolojim açıktı. Soru, sonlandırıcıları ve yıkıcıları bir araya getirmekteydi ve hatta aynı nefeste bertarafçılardan bahsetti. Doğru kelimeleri yaymanın faydası var.
kdbanman

@kdbanman Sorun, cevabınız referans olarak durduğu için ikinize de hitap etmem oldu. Ref sayısını paradigmatik bir örnek olarak kullanamazsınız, çünkü nadiren dillerde kullanılan (OP tarafından belirtilen dilleri kontrol edin) zayıf bir GC olduğu için kesinleştiricilerin eklenmesinin gerçekten kolay olacağı (ancak sınırlı kullanımı ile). İzleme kollektörleri neredeyse her zaman kullanılır. Ancak sonlandırıcılar bunlara bağlı kalmak zordur, çünkü ölen nesneler bilinmemektedir (doğru olduğunu düşündüğünüz ifadenin aksine). Statik ve dinamik yazım arasındaki ayrım önemsizdir, veri deposunun dinamik yazılması esastır.
babou

@kdbanman Terminoloji ile ilgili olarak, genel olarak, farklı durumlara karşılık geldiğinde yararlıdır. Fakat burada yardımcı olmuyor, çünkü soru sonlandırmayı GC'ye aktarmakla ilgili. Temel GC'nin sadece yıkımı yapması gerekiyordu. İhtiyaç duyulan şey ayırt edilen bir terminolojiyle getting memory recycleddediğim, reclamationve yapıyor bazı temizlik bundan önce, bu tür diğer kaynakları geri ödemelerine ya da ben dediğimiz nesne tablolar, güncelleme gibi finalization. Bunlar bana alakalı meseleler gibiydi, fakat terminolojinizde benim için yeni olan bir noktayı kaçırmış olabilirim.
babou

1
Teşekkürler @kdbanman, babou. İyi tartışma. Sanırım yazılarınızın ikisinde de benzer hususlar var. İkinizin de belirttiği gibi, asıl mesele dilin çalışma zamanında kullanılan çöp toplayıcı kategorisi gibi görünüyor. Benim için bazı yanlış anlamaları gideren bu makaleyi buldum . Daha sağlam olan gcs, yalnızca düşük seviyeli ham belleği kullanır ve bu da gc'ye opak olmayan daha yüksek seviye nesne türleri verir. Dahili bellekten haberi olmadan gc nesneleri yok edemez. Bu senin sonucun gibi görünüyor.
dbcb

4

Nesne yıkıcı paterni, sistem programlamasında hata işleme için temeldir, ancak çöp toplama ile ilgisi yoktur. Aksine, nesne ömrünü bir kapsamla eşleştirmekle ilgili olmalı ve birinci sınıf fonksiyonlara sahip herhangi bir dilde uygulanabilir / kullanılabilir.

Örnek (sözde kodu). Posix dosya tanımlayıcı türü gibi bir "ham dosya" türünüz olduğunu varsayalım. Dört temel işlemler vardır, open(), close(), read(), write(). Her zaman kendisinden sonra temizleyen "güvenli" bir dosya türü uygulamak istersiniz. (Yani, otomatik bir kurucu ve yıkıcı vardır.)

Bizim dil ile istisna kullanımı vardır varsayıyoruz throw, tryve finally(eğer bir tür kullanıcı bir hata belirtmek için özel bir değer döndüren bir disiplin kurabilirsiniz taşıma istisnasız dilde.)

İşi yapan bir işlevi kabul eden bir işlev ayarlarsınız. Worker işlevi bir argüman kabul eder ("safe" dosyasının tanıtıcısı).

with_file_opened_for_read (string:   filename,
                           function: worker_function(safe_file f)):
  raw_file rf = open(filename, O_RDONLY)
  if rf == error:
    throw File_Open_Error

  try:
    worker_function(rf)
  finally:
    close(rf)

Ayrıca uygulamalarını sağlamak read()ve write()için safe_file(sadece dediğimiz raw_file read()ve write()). Şimdi kullanıcı safe_fileböyle bir tür kullanır :

...
with_file_opened_for_read ("myfile.txt",
                           anonymous_function(safe_file f):
                             mytext = read(f)
                             ... (including perhaps throwing an error)
                          )

C ++ yıkıcısı gerçekten bir try-finallyblok için sadece sözdizimsel bir şeker . Burada yaptığım hemen hemen safe_filebir yapıcı ve yıkıcı ile bir C ++ sınıfı ne derleyeceğini dönüştürmek. C ++ 'nın finallyistisnaları olmadığını, özellikle de Stroustrup'un açık bir yıkıcı kullanmanın sözdizimsel olarak daha iyi olduğunu düşündüğünü (ve dilin adsız işlevleri olmadan önce dile getirdiğini) unutmayın.

(Bu, insanların Lisp benzeri dillerde uzun yıllardır hata işlemede bulunma yöntemlerinden birinin basitleştirilmesidir. Sanırım ilk kez 1980'lerin sonlarında veya 1990'ların başında rastladım, ama nerede olduğunu hatırlamıyorum.)


Bu, C ++ 'da yığına dayalı yıkıcı modelinin iç kısımlarını açıklar, ancak toplanan bir dilin neden böyle bir işlevi yerine getirmediğini açıklamaz. Bunun çöp toplama ile ilgisi olmadığı doğru olabilir, ancak çöp toplanan dillerde zor veya verimsiz görünen genel nesne imhası / sonlandırmasıyla ilgili olabilir. Yani, genel yıkım desteklenmiyorsa, istif temelli yıkım da ihmal edilmiş görünüyor.
dbcb

Başta söylediğim gibi: birinci sınıf işlevlere sahip (ya da birinci sınıf işlevlerin bir miktarına yakınlığı olan) herhangi bir çöp toplanmış dil, size gibi safe_fileve with_file_opened_for_read(kapsam dışına çıktığında kendisini kapatan bir nesne gibi "kurşun geçirmez" arabirimler sağlama yeteneği verir . ). Önemli olan şey, yapıcıların aynı sözdizimine sahip olmamasıdır. Lisp, Scheme, Java, Scala, Go, Haskell, Rust, Javascript, Clojure hepsi yeterli birinci sınıf fonksiyonları desteklediğinden, aynı kullanışlı özelliği sağlamak için yıkıcılara ihtiyaçları yoktur.
Wandering Logic,

Sanırım ne dediğini anladım. Dil, yıkıcı benzeri işlevselliği manüel olarak uygulamak için temel yapı taşlarını (dene / yakala / son, birinci sınıf işlevler, vb.) Sağladığından, yıkıcılara gerek duymazlar mı? Sadeliği nedeniyle bu rotayı izleyen bazı dilleri görebiliyordum. Buna rağmen, listelenen tüm dillerin birincil nedeni olma olasılığı düşük görünmektedir, ancak belki de budur. Belki de ben sadece C ++ yıkıcılarını seven ve aslında hiç kimsenin umursamadığı büyük azınlıktayım, bu da çoğu dilin yıkıcı uygulamamasının nedeni olabilir. Sadece umursamıyorlar.
dbcb

2

Bu, sorunun tam bir cevabı değil, ancak diğer cevaplarda veya yorumlarda yer almayan birkaç gözlem eklemek istedim.

  1. Soru, örtük olarak, kendisini sınırlayan bir Simula tarzı nesne yönelimli dilden bahsettiğimizi varsayar. Çoğu dilde, hatta nesnelere sahip olanlarda bile, her şey bir nesne değildir. Yıkıcıları uygulayan makine, her dil uygulayıcısının ödemeye istekli olmadığı bir maliyet getirecektir.

  2. C ++ 'ın imha emriyle ilgili kesin garantileri vardır. Ağaç gibi bir veri yapınız varsa, örneğin, çocuklar ebeveynlerinden önce imha edilecektir. Bu GC'd dillerinde geçerli değildir, bu yüzden hiyerarşik kaynaklar öngörülemeyen bir düzende serbest bırakılabilir. Bellek dışı kaynaklar için bu önemli olabilir.


2

En popüler iki GC çerçevesinden ikisi (Java ve .NET) tasarlanırken, yazarların sonlandırmanın diğer kaynak yönetimi biçimlerine olan ihtiyacı önlemek için yeterince iyi çalışacağını düşündüğünü düşünüyorum. % 100 güvenilir ve deterministik kaynak yönetimi için gerekli tüm özelliklere ihtiyaç duyulmaması durumunda, dilin ve çerçeve tasarımının birçok yönü büyük ölçüde basitleştirilebilir. C ++ 'da şu kavramları ayırt etmek gerekir:

  1. Yalnızca referans sahibine ait olan ve sahibinin bilmediği herhangi bir işaretçi / referans tarafından tanımlanmayan bir nesneyi tanımlayan işaretçi / başvuru.

  2. Yalnızca kimseye ait olmayan paylaşılabilir bir nesneyi tanımlayan işaretçi / başvuru.

  3. İşaretçi / başvuru bu tanımlar özel bir amacı ait referans sahibi tarafından, fakat için "görüntüsü" kitabı izlemenin bir yol vardır erişilebilir olabilir.

  4. Başkasına ait olan bir nesnenin görünümünü sağlayan bir nesneyi tanımlayan işaretçi / başvuru.

Bir GC dili / çerçevesi kaynak yönetimi konusunda endişelenmek zorunda değilse, yukarıdakilerin tümü tek bir referansla değiştirilebilir.

Kesinleşmenin diğer kaynak yönetimi türlerine olan ihtiyacı ortadan kaldıracağı fikrini saf buluyorum, ancak böyle bir beklentinin o zamanlar makul olup olmadığı, tarihin kesinleşmeden daha kesin kaynak yönetimi gerektiren birçok vaka olduğunu göstermiştir. . Dil / çerçeve düzeyinde mülkiyeti tanımanın ödülünün maliyeti doğrulamak için yeterli olacağını düşünüyorum (karmaşıklığın bir yerde olması ve dil / çerçeveye taşınmasının kullanıcı kodunu basitleştireceğini) ancak önemli olduğunu kabul ediyorum. Tek bir "tür" referansa sahip olmanın faydalarını tasarlayın - yalnızca dil / çerçeve kaynak temizliği konularına uyarsa işe yarar.


2

Neden çöp toplanan dillerdeki yıkıcı paradigması yaygın olarak bulunmuyor?

C ++ arkaplanından geliyorum, bu yüzden bu alan benim için şaşırtıcı.

C ++ 'daki yıkıcı aslında iki şeyi birleştiriyor. RAM'i serbest bırakır ve kaynak kimliklerini serbest bırakır.

Diğer diller ayrı GC kaynak kimlikleri azat başka bir dil özelliği almak şarj ederken RAM kurtararak sorumlu olmak suretiyle bu kaygıları.

Bu dillerin hafızayı yönetmeye değer tek kaynak olarak gördüğünü çok az buluyorum.

GC'lerin hepsi bu işte. Onlar sadece bir şey yapmazlar ve hafızanızın tükenmemesini sağlamaktır. Eğer RAM sonsuzsa, artık var olmaları için gerçek bir sebep olmadığından tüm GC'ler emekli olacaktır.

Peki ya yuvalar, dosya tutamaçları, uygulama durumları?

Diller, kaynak kimliklerini serbest bırakmanın farklı yollarını sağlayabilir:

  • manuel .CloseOrDispose()kod boyunca dağınık

  • manuel .CloseOrDispose()" finallyblok" el kitabının içine dağılmış

  • Manuel "kaynak kimliği blokları" (yani using, with, try-with-kaynaklar Otomatikleştirir, vs) .CloseOrDispose()blok sonra yapılan

  • Otomatikleştirir garantili "kaynak kimliği blokları".CloseOrDispose() blok sonra yapılan

Pek çok dil, kaynağın yanlış yönetilmesi için fırsat yaratan el kitabı (garantinin aksine) kullanır. Bu basit NodeJS kodunu alın:

require('fs').openSync('file1.txt', 'w');
// forget to .closeSync the opened file

.. nerede programcı açılan dosyayı kapatmayı unuttu.

Program çalışmaya devam ettiği sürece, açılan dosya limbo içinde kalmış olacaktır. HxD kullanarak dosyayı açmaya çalışarak ve yapılamayacağını doğrulayarak bunu doğrulamak kolaydır:

görüntü tanımını buraya girin

Kaynak kimliklerini C ++ yıkıcıları içinde serbest bırakmak da garanti edilmez. RAII 'nin garantili "kaynak kimliği blokları" gibi çalıştığını düşünebilirsiniz, ancak "kaynak kimliği blokları" ndan farklı olarak, C ++ dili, RAII bloğunun sızdırılmasını önleyen nesneyi durdurmaz , böylece RAII bloğu asla yapılamaz .


Neredeyse bütün modern çöpler, Ruby, Javascript / ES6 / ES7, Actionscript, Lua, vb. Gibi OOPy nesne desteğiyle dilleri topladı. Python sınıf __del__()metodu ile sadece bir tek gibi görünüyor . Bu neden?

Çünkü kaynak kimliklerini yukarıda da belirtildiği gibi başka yollarla yönetiyorlar.

Nesne imhasında özel bir mantık yürütme imkânı bu dillere götüren dil tasarım kararları nelerdir?

Çünkü kaynak kimliklerini yukarıda da belirtildiği gibi başka yollarla yönetiyorlar.

Neden bir dil, özel örnekleme (yapıcılar) ile birlikte sınıf veya sınıf benzeri yapılara sahip yerleşik nesne örnekleri kavramına sahip olsa da, imha etme / sonlandırma işlevselliğini tamamen göz ardı etsin?

Çünkü kaynak kimliklerini yukarıda da belirtildiği gibi başka yollarla yönetiyorlar.

Yıkıcı / sonlandırıcının gelecekte belirsiz bir süreye kadar çağrılmayacağı, ancak Java veya Python'un işlevselliği desteklemesini durduramayacağına dair olası bir argüman görebiliyordum.

Java'nın yıkıcıları yok.

Java dokümanlar söz :

Ancak, sonuçlandırmanın genel amacı, nesne geri alınamaz bir şekilde atılmadan önce temizleme işlemlerini gerçekleştirmektir. Örneğin, bir giriş / çıkış bağlantısını temsil eden bir nesnenin sonlandırma yöntemi, nesne kalıcı olarak atılmadan önce bağlantıyı kesmek için açık G / Ç işlemleri gerçekleştirebilir.

..ama kaynak kimliği yönetim kodunu içine koymak Object.finalizerbüyük oranda bir anti-kalıp olarak kabul edilir ( çapraz başvuru ). Bu kodun yerine çağrı sitesine yazılmalıdır.

Anti-paterni kullanan insanlar için gerekçeleri, çağrı merkezindeki kaynak kimliklerini serbest bırakmayı unutmuş olmalarıdır. Böylece, kesinleşirse, yine durumunda, tekrar yaparlar .

Herhangi bir nesne sonlandırma biçimini desteklememek için ana dil tasarım nedenleri nelerdir?

Sonlandırıcılar için çok fazla kullanım durumu yoktur, çünkü nesneye artık güçlü referansların olmadığı zamanlar ve hafızasının GC tarafından geri alındığı zamanlar arasında bir kod parçası çalıştırmak için kullanılırlar.

Muhtemel bir kullanım örneği, nesne tarafından GC tarafından toplanan ve nesneye artık güçlü referanslar kalmayan zaman arasındaki günlük kaydını tutmak istediğiniz zaman:

finalize() {
    Log(TimeNow() + ". Obj " + toString() + " is going to be memory-collected soon!"); // "soon"
}

-1

Bunun üzerine, yıkıcıların uygulandıkları bir dilde sorunlu olduğunu iddia eden daha genel fikirleri olan Dr Dobbs wrt c ++ 'da bir refere buldular. Buradaki kaba bir fikir, yıkıcıların temel amaçlarından biri, hafızanın yeniden tahsis edilmesinin üstesinden gelmek olduğu ve doğru şekilde başarılması zor gibi görünüyor. hafıza parçalı olarak tahsis edilir, ancak farklı nesneler birbirine bağlanır ve sonra ayrılma sorumluluğu / sınırları çok net değildir.

bu nedenle, bir çöp toplayıcısının buna çözümü yıllar önce gelişti, ancak çöp toplama yönteminde kapsamdan kaybolan nesnelere (yani uygulanması zor olan kavramsal bir fikirdir) değil, periyodik olarak, biraz da olsa, özelikle çalışan bir koleksiyoncuya dayanır. Uygulamanın "hafıza baskısı" (yani hafıza tükeniyor) ile karşılaştığında.

Başka bir deyişle, "yeni kullanılmamış bir nesnenin" yalnızca insan kavramı aslında hiçbir şekilde "hiçbir zaman" bir anda "kullanılmayacak şekilde anlamında yanıltıcı bir soyutlamadır. kullanılmayan nesneler, yalnızca nesne referans grafiğini geçen ve en iyi performans gösteren algoritmalar aralıklı olarak çalışan bir çöp toplama algoritması çalıştırarak "keşfedilebilir".

kullanılmayan nesneleri hemen anında tanımlayabilen, daha sonra tutarlı yıkıcı çağrı koduna yol açabilecek, ancak bölgede uzun yıllar süren araştırmalardan sonra bulunamayan keşfedilmeyi bekleyen daha iyi bir çöp toplama algoritması vardır.

dosyalar veya bağlantılar gibi kaynak yönetimi alanlarına yönelik çözüm, kullanımlarını ele almaya çalışan "yöneticiler" nesnesine sahip görünüyor.


2
İlginç bul Teşekkürler. Yazarın argümanı, sınıfın uygun bir kopya yapıcısına sahip olmadığı (gerçek bir sorun olan) sınıf örneklerinin değere göre geçilmesi nedeniyle yıkıcıya yanlış zamanda çağrılmaya dayanmaktadır. Ancak, bu senaryo modern dinamik dillerin çoğunda gerçekten mevcut değildir, çünkü her şey yazarın durumunu önleyen her şeyden kaynaklanmaktadır. Bu ilginç bir bakış açısı olmasına rağmen, çoğu çöp toplanan dilin neden yıkıcı / sonlandırma işlevini atlamayı seçtiğini açıkladığını sanmıyorum.
dbcb

2
Bu cevap Dr. Dobb'un makalesini yanlış tanıtıyor: makale, yıkıcıların genel olarak sorunlu olduğunu iddia etmiyor. Makale aslında şunu savunuyor: Bellek yönetimi ilkelleri ifadelere benziyor, çünkü ikisi de basit ama çok güçlü. Aynı şekilde, en iyi ifadelerin "uygun şekilde sınırlı kontrol yapılarında" (Bkz: Dijktsra) en iyi şekilde kapsüllenmesiyle aynı şekilde, bellek yönetimi ilkeleri en iyi şekilde "uygun biçimde sınırlı veri yapılarında" kapsüllenmiştir. Yıkıcılar bu yönde bir adım, ancak yeterince uzak değil. Bunun doğru olup olmadığına kendin karar ver.
kdbanman
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.