Deterministik Olmayan Kaynak Yönetimi Sızdıran Bir Soyutlama mı?


9

Görebildiğim kadarıyla, kaynak yönetiminin iki yaygın biçimi vardır: deterministik yıkım ve açık. İlki örnekleri C ++ yıkıcıları ve akıllı işaretçiler ya da Perl'in DESTROY alt grubu, ikincisinin bir örneği Ruby'nin kaynakları yönetecek blok paradigması ya da .NET'in IDispose arayüzü olacaktır.

Yeni diller, belki de referans olmayan sayma çeşitliliğinin çöp toplama sistemlerinin kullanılmasının bir yan etkisi olarak, ikincisini tercih ediyor gibi görünmektedir.

Benim sorum şudur: akıllı işaretçiler veya referans sayma çöp toplama sistemleri için yıkıcıların - hemen hemen aynı şey - örtük ve şeffaf kaynak yıkımına izin verdiği göz önüne alındığında, açıklığa dayanan deterministik olmayan türlerden daha az sızıntılı bir soyutlamadır. notasyonu?

Somut bir örnek vereceğim. Tek bir üst sınıfın üç C ++ alt sınıfınız varsa, herhangi bir özel yıkıma ihtiyaç duymayan bir uygulama olabilir. Belki de büyüsünü başka bir şekilde yapar. Özel bir yıkıma ihtiyaç duymaması, önemsizdir - tüm alt sınıflar hala aynı şekilde kullanılmaktadır.

Başka bir örnek Ruby bloklarını kullanır. İki alt sınıfın kaynakları serbest bırakması gerekir, bu nedenle üst sınıf, özel bir yıkım gerektirmedikleri için diğer belirli alt sınıfların buna ihtiyaç duymamasına rağmen, yapıcıda bir blok kullanan bir arabirimi seçer.

İkincisi, kaynak imhasının uygulama detaylarını sızdırırken, birincisi yapmıyor mu?

DÜZENLEME: Diyelim ki Ruby'den Perl'e göre daha adil olabilir, çünkü biri deterministik yıkıma sahip, diğeri ise çöp toplama.


5
"Evet" demeye cazip geldim, ama başkalarının bu konuda söylediklerini duymayı seviyorum.
Bart van Ingen Schenau

Şeffaf kaynak tahribatı? Normal işaretçiler yerine akıllı işaretçiler kullanmak zorunda olmanız dışında? Bu nesneleri erişmek için sadece bir mekanizma (referanslar) daha şeffaf olduğunu düşünmüyorum (C ++ 'da en az dört veya beş var).
Giorgio

@Giorgio: "Bir nesneye erişim yolları" oldukça belirsizdir. Yani okumak veya yazmak mı? Sabit / Uçucu nitelik? İşaretçiler gerçekten "bir nesneye erişmenin bir yolu" değildir; Hemen hemen her ifade bir nesneye neden olur ve bir işaretçiyi silme işlemi o kadar da özel değildir.
MSalters

1
@Giorgio: OOP anlamda, bir C ++ işaretçisine mesaj gönderemezsiniz. Mesajı göndermek için işaretçiyi (*ptr).Message()ya da eşdeğer bir şekilde göndermeniz gerekir ptr->Message(). ((*ptr))->MessageEşdeğer olduğu gibi , izin verilen sonsuz sayıda ifade vardır . Ama hepsi kaynıyorexpressionIdentifyingAnObject.Message()
MSalters

1
Yeniden sayım ile dairelerden kaçınmaya dikkat etmelisiniz. Böylece soyutlama da farklı bir şekilde sızıyor.
CodesInChaos

Yanıtlar:


2

Kendi örneğiniz soruyu cevaplıyor. Şeffaf yıkım, açık yıkımdan açıkça daha az sızıntılıdır. Sızıntı yapabilir, ancak daha az sızıntı yapar.

Açık yıkım, tüm tuzaklarla birlikte C'de malloc / free ile benzerdir. Belki de kapsama dayalı görünmesini sağlamak için bazı sözdizimsel şekerle.

Şeffaf tahribatın açık olarak faydalarından bazıları: -
aynı kullanım şekli -
kaynağı serbest bırakmayı unutamazsınız.
--Temizlik detayları kullanım noktasında manzarayı atmaz.


2

Soyutlamadaki başarısızlık aslında çöp toplama işleminin deterministik olmadığı gerçeği değil, nesnelerin referans aldıkları şeylerle "ilgilendikleri" ve sahip olmadıkları şeylerle ilgilenmedikleri fikridir. Referanslar. Nedenini görmek için, belirli bir kontrolün ne sıklıkta boyanacağını gösteren bir nesnenin senaryosunu düşünün. Yaratılışta, kontrolün "boya" olayına abone olur ve elden çıkarıldığında abonelikten çıkar. Click olayı yalnızca bir alanı artırır ve bir yöntem getTotalClicks()o alanın değerini döndürür.

Sayaç nesnesi oluşturulduğunda, kendisine yapılan bir referansın izlediği kontrol içinde saklanmasına neden olmalıdır. Kontrol, sayaç nesnesini gerçekten umursamıyor ve sayaç nesnesinin ve ona yapılan başvurunun varlığını bırakması kadar mutlu olurdu, ancak referans var olduğu sürece, her seferinde bu nesnenin olay işleyicisini çağırır. kendini boyar. Bu eylem kontrol için tamamen işe yaramaz, ancak getTotalClicks()nesneyi çağıran herkes için yararlı olacaktır .

Örneğin bir yöntem yeni bir "boya sayacı" nesnesi oluşturmak, kontrol üzerinde biraz eylem yapmak, kontrolün kaç kez yeniden boyandığını gözlemlemek ve daha sonra boya sayacı nesnesini terk etmek olsaydı, nesne olaya bile abone olmaya devam ederdi yine de hiç kimse nesnenin ve ona yapılan tüm referansların yok olup olmadığını umursamazdı. Ancak nesneler, denetimin kendisi oluncaya kadar toplama için uygun olmaz. Yöntem, denetimin ömrü boyunca binlerce kez çağrılacak bir yöntem olsaydı [makul bir senaryo], bellek taşmasına neden olabilir, ancak N çağrılarının maliyetinin muhtemelen O (N ^ 2) veya O (N ^ 3), abonelik işleme çok verimli olmadıkça ve çoğu işlem aslında herhangi bir resim içermiyorsa.

Bu özel senaryo, kontrolün güçlü bir nesneden ziyade karşı nesneye zayıf bir referans göstermesini sağlayarak ele alınabilir. Zayıf bir abonelik modeli yardımcı olur, ancak genel durumda çalışmaz. Diyelim ki tek bir denetimden tek bir olayı izleyen bir nesneye sahip olmak yerine, birden fazla denetimi izleyen bir olay günlüğü nesnesine sahip olmak istedim ve sistemin olay işleme mekanizması, her denetimin bir referansa ihtiyaç duyduğu farklı bir olay günlüğü nesnesine. Bu durumda, bir denetimi olay günlüğüne bağlayan nesne yalnızca her ikisinin dedenetlenir ve olay günlüğü yararlı kalır. Ne kontrol ne de olay kaydedici link olayı için güçlü bir referansa sahip değilse, hala "yararlı" olmasına rağmen varlığını sona erdirecektir. Her ikisinden de güçlü bir olay varsa, bağlantı nesnesinin ömrü, diğeri ölse bile gereksiz yere uzatılabilir.

Evrenin herhangi bir yerinde bir nesneye atıf yoksa, nesne güvenli bir şekilde işe yaramaz olarak kabul edilebilir ve varlıktan çıkarılabilir. Bununla birlikte, bir nesneye referans olması, nesnenin "yararlı" olduğu anlamına gelmez. Birçok durumda, nesnelerin gerçek kullanışlılığı , GC perspektifinden - kendileriyle tamamen ilgisiz olan diğer nesnelere yapılan göndermelerin varlığına bağlı olacaktır .

Kimse ilgilenmediği zaman nesneler deterministik olarak bildirilirse, bu bilgiden faydalanacak olan herkesin bilgilendirildiğinden emin olmak için bu bilgiyi kullanabilirler. Bununla birlikte, bu bildirimin yokluğunda, yalnızca bu referanslara eklenmiş anlamsal anlamı değil, sadece var olan referanslar kümesini biliyorsa, hangi nesnelerin "yararlı" olarak kabul edildiğini belirlemenin genel bir yolu yoktur. Dolayısıyla, referansların varlığının veya yokluğunun otomatik kaynak yönetimi için yeterli olduğunu varsayan herhangi bir model, GC nesne terkini anında tespit edebilse bile mahkum olacaktır.


0

Hayır "yıkıcı, ya da" bu sınıfın yok edilmesi gerekir "diyen başka bir arayüz, bu arayüzün bir sözleşmesidir. Özel yıkım gerektirmeyen bir alt tip yaparsanız, Liskov İkame Prensibi'nin ihlal edildiğini düşünmeye meyilli olurum .

C ++ ve diğerlerine gelince, çok fazla fark yoktur. C ++ bu arabirimi tüm nesneleri üzerinde zorlar. Dil tarafından istendiğinde soyutlamalar sızamaz.


4
"Özel yıkım gerektirmeyen bir alt tip yaparsanız" Bu bir LSP ihlali değildir, çünkü op-yok geçerli bir özel yıkım vakasıdır. Sorun, türetilmiş bir sınıfa imha gereksinimi eklediğinizde.
CodesInChaos

Burada kafam karışıyor. Bir C ++ alt sınıfına özel imha kodu eklemek gerekirse, kullanım kalıplarını hiç değiştirmez, çünkü otomatiktir. Bu, üst sınıf ve alt sınıfın yine de birbirinin yerine kullanılabileceği anlamına gelir. Ancak, kaynak yönetimi için açık bir gösterimle, açık bir yıkıma ihtiyaç duyan bir alt sınıf, kullanımının bir üst sınıfla uyumsuz olmasını sağlar, değil mi? (Üst sınıfın açık yıkıma ihtiyacı olmadığını varsayarsak.)
Louis Jackman

@CodesInChaos - ah evet, sanırım bu doğru.
Telastyn

@ljackman: Özel yıkım gerektiren bir sınıf, yapıcısını gerçekleştirmesini sağlamak için yapıcısını çağıran kişiye yük getirir. Bu bir LSP ihlali oluşturmaz, çünkü DerivedFooThatRequiresSpecialDestructionyalnızca çağıran kodla oluşturulabilir new DerivedFooThatRequiresSpecialDestruction(). Öte yandan, DerivedFooThatRequiresSpecialDestructionimha gerektiren bir şey beklemeyen bir koda dönen bir fabrika yöntemi bir LSP ihlali olacaktır.
supercat

0

Benim sorum şudur: akıllı işaretçiler veya referans sayma çöp toplama sistemleri için yıkıcıların - hemen hemen aynı şey - örtük ve şeffaf kaynak yıkımına izin verdiği göz önüne alındığında, açıklığa dayanan deterministik olmayan türlerden daha az sızıntılı bir soyutlamadır. notasyonu?

Çevrimleri elle izlemek zorunda kalmak ne örtük ne de şeffaftır. Tek istisna, tasarımla döngüleri yasaklayan bir dili olan bir referans sayma sistemidir. Erlang böyle bir sisteme örnek olabilir.

Yani her iki yaklaşım da sızdı. Temel fark, yıkıcıların C ++ 'da her yere sızması, ancak IDispose.NET'te çok nadir olmasıdır.


1
Dışında döngüleri son derece nadirdir ve açıkça döngüsel olan veri yapıları dışında pratikte asla gerçekleşmez. Temel fark, C ++ 'daki yıkıcıların her yerde düzgün bir şekilde ele alınması, ancak IDispose'in .NET'teki sorunu nadiren ele almasıdır.
DeadMG

"Döngüler dışında son derece nadirdir". Modern dillerde mi? Bu hipoteze meydan okurum.
Jon Harrop
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.