Kapsamlı bellek yönetiminin dezavantajları


38

Gerçekten kapsam tabanlı bellek yönetimi (SBMM) veya benzeri de ray olduğu gibi, daha yaygın (karışıklığa?) C ++ topluluğu tarafından anılacaktır. Bildiğim kadarıyla, C ++ (ve C) dışında, günümüzde SBMM / RAII'yi ana bellek yönetim mekanizması yapan başka bir ana dil yoktur ve bunun yerine çöp toplama (GC) kullanmayı tercih ederler.

Bunu kafa karıştırıcı buluyorum, çünkü

  1. SBMM programları daha belirleyici kılar (bir nesnenin ne zaman imha edildiğini tam olarak söyleyebilirsiniz);
  2. GC'yi kullanan dillerde, genellikle GC'nin amacını kısmen yenen ve aynı zamanda hataya açık olan manuel kaynak yönetimi (örneğin, Java'daki dosyaları kapatma) yapmanız gerekir;
  3. Yığın hafızası ayrıca (çok zarif, imo) kapsama bağlı olabilir (bkz std::shared_ptr. C ++).

SBMM neden daha yaygın kullanılmıyor? Dezavantajları nelerdir?


1
Bazı dezavantajlar (özellikle hız ile ilgili) Vikipedi'de tartışılıyor: en.wikipedia.org/wiki/…
Philipp

2
Java'nın manuel kaynak yönetimi sorunu, finalize()çöp toplama işleminden önce bir nesnenin yönteminin çağrılacağını garanti etmenin bir yan etkisidir . Aslında bu, çöp toplamanın çözmesi gereken aynı problem sınıfını yaratır.
Blrfl

7
@Blrfl Saçmalama. “Manuel” kaynak yönetimi (bellek dışındaki kaynaklar için, açıkçası), bu “sorun” olmasa bile tercih edilir, çünkü GC, kaynak kullanılmadığında ya da hiç çalıştırılmadığında çok uzun süre çalışabilir. Bu bellek için bir sorun değil ve bellek yönetimi çöp toplama işleminin çözmesi gereken tek şey.

4
Btw. SBRM olarak adlandırmak hoşuma gidiyor, çünkü genel olarak kaynakları yönetmek için aynı mekanizmayı sadece hafızayı kullanabiliyorsunuz.
PlazmaHH

Yanıtlar:


27

Bu belleğin, diğer tüm kaynaklardan bir araya geldiğinden daha yaygın (düzinelerce, yüzlerce hatta binlerce kez) olduğunu ileri sürerek başlayalım. Her değişken, nesne, nesne üyesi kendisine tahsis edilmiş ve daha sonra serbest bırakılan bir belleğe ihtiyaç duyar. Açtığınız her dosya için, dosyadan çıkartılan verileri depolamak için düzinelerce ila milyonlarca nesne oluşturursunuz. Her TCP akışı, akışa yazılmak üzere oluşturulan sınırsız sayıda geçici bayt dizesi ile birlikte gider. Burada aynı sayfada mıyız? Harika.

RAII'nin çalışması için (güneşin altındaki her kullanım için hazır akıllı işaretçileriniz olsa bile) mülkiyeti doğru yapmanız gerekir . Bu nesneye kimlerin sahip olması gerektiğini, kimin yapmaması gerektiğini ve sahipliğin A'dan B'ye devredilmesi gerektiğini analiz etmeniz gerekir. Elbette, her şey için ortak mülkiyeti kullanabilirsiniz , ancak daha sonra akıllı işaretçiler üzerinden bir GC taklit ediyor olacaksınız. Bu noktada , GC'yi dilde oluşturmak çok daha kolay ve daha hızlı hale gelir .

Çöp toplama sizi bu kaygıdan en çok kullanılan kaynak bellekten kurtarır. Tabii, yine de diğer kaynaklar için aynı kararı vermeniz gerekir, ancak bunlar çok daha az yaygındır (yukarıya bakın) ve karmaşık (örneğin paylaşılan) sahiplik de daha az yaygındır. Zihinsel yük önemli ölçüde azalır.

Şimdi, tüm değerlerin çöp toplanması için bazı olumsuz yönleri adlandırıyorsunuz. Ancak, her iki bellek güvenli GC entegre ve tek dile de ray ile değer türleri belkide o diğer yollarla bu tavizlere migitate daha iyidir, son derece zor?

Determinizm kaybı, pratikte o kadar da kötü değildir, çünkü sadece deterministik nesnenin ömrünü etkiler . Bir sonraki paragrafta açıklandığı gibi, çoğu kaynak (bellekten ayrı, bol miktarda ve tembel olarak geri dönüştürülebilen), bu dillerde ömür boyu nesneye bağlı değildir . Birkaç başka kullanım senaryosu da var, fakat deneyimlerime nadir rastlanırlar.

İkinci nokta, manuel kaynak yönetimi, günümüzde kapsam tabanlı temizlik yapan bir ifadeyle ele alınmaktadır, ancak bu temizliği nesne ömrü boyunca birleştirmemektedir (dolayısıyla GC ve bellek güvenliği ile etkileşime girmez). Bu usingC #, withPython'da try-with-Java ile son sürümleridir.


1
Bu neden GC gibi klasik olmayan modellerin deterministik olanlardan daha üstün olması gerektiğini açıklamıyor. usingifadeler sadece yerel olarak mümkündür. Üye değişkenlerinde tutulan kaynakları bu şekilde temizlemek mümkün değildir.
Philipp

8
Her şey için @Philipp Paylaşılan sahiplik olan bir GC, sadece çok zayıf bir. Ref saymayı ima etmek için "paylaşılan mülkiyeti" kabul ederseniz, lütfen öyle söyleyin, ancak amon'un cevabındaki yorumlarda döngüler hakkındaki tartışmaya devam edin. Ayrıca, ref sayımının OP'nin ilgilendiği anlamda deterministik olup olmadığından da emin değilim (nesneler olabildiğince erken serbest bırakılır, indirim döngüleri vardır, ancak bunun ne zaman programa bakarak olduğunu söyleyemezsiniz). Ayrıca, her şey için ref sayımı, modern izleme GC'sinden çok daha yavaştır.

16
usingRAII'ye kıyasla bir şaka, bilirsin.
DeadMG

3
@Philipp, lütfen "superior" için ölçünüzü tanımlayın. Manuel bellek yönetiminin bellek yönetimi ile uğraşırken çalışma zamanında daha hızlı olduğu doğrudur. Ancak, yazılımın maliyeti yalnızca yalnızca bellek yönetimi için harcanan CPU süresi üzerinden değerlendirilemez.
Saat

2
@ArTs: Buna kesinlikle katılıyorum bile. RAII, bir nesneyi kapsam bıraktıkça imha edilmesi gerekliliği şartıyla birlikte gelir. Nesne imhaları yapmak için bir döngü gereklidir. Modern bir nesiller GC'de, bu tahripler döngünün sonuna kadar ertelenebilir ve hatta daha sonra ertelenebilir ve hafızaya değer yüzlerce yinelemenin yok edilmesi için tek bir işlem gerçekleştirilebilir. Bir GC , iyi durumlarda çok çok hızlı olabilir .
Phoshi

14

RAII ayrıca otomatik referans sayma bellek yönetiminden de gelir; örneğin Perl tarafından kullanıldığı gibi. Referans sayımının uygulanması kolay, deterministik ve oldukça performanslı olsa da, genel olarak kullanılmadığından dairesel referanslarla (sızıntıya neden olurlar) ilgilenemez.

Çöp-Toplanan diller olamaz doğrudan RAII kullanmak, ama çoğu zaman eşdeğer etkisi ile teklif sözdizimi yapmak. Java’da, with-ressource ifadesi var

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

otomatik .close()olarak blok çıkışındaki kaynağı çağırır . C # ifadesi ayrılırken çağrılmasına IDisposableizin veren bir arayüze sahiptir . Python ifadesine sahiptir:.Dispose()using (...) { ... }with

with open(filename) as f:
    ...

benzer şekilde çalışır. Bu konuda ilginç bir dönüş, Ruby'nin dosya açma yöntemi geri çağrı alır. Geri arama yapıldıktan sonra dosya kapatılır.

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

Node.js aynı stratejiyi kullandığını düşünüyorum.


4
Kaynak yönetimi için üst düzey işlevler kullanmak, Ruby'den çok daha önce uzanıyor. Lisps'ta, örneğin dosyayı açan, onu bir with-open-filehandleişleve getiren işlevlere sahip olmak oldukça yaygındır ve işlevin geri dönmesi üzerine dosyayı tekrar kapatın.
Jörg W Mittag

4
Döngüsel referans argümanı oldukça yaygındır, ancak gerçekte ne kadar önemlidir? Mülkiyet açıksa, döngüsel referanslar zayıf işaretçiler kullanılarak hafifletilebilir.
Philipp

2
@Philipp Ref saymayı kullandığınızda, sahiplik genellikle açık değildir . Ayrıca, bu cevap yalnızca ve otomatik olarak sayma kullanan diller hakkında konuşur, bu nedenle zayıf referanslar yoktur veya güçlü referanslardan çok daha zordur.

3
@Philipp döngüsel veri yapıları yine de karmaşık grafiklerle çalışmadığınız sürece çok nadir görülür. Zayıf işaretçiler genel bir döngüsel nesne grafiğine yardımcı olmamakla birlikte, bir ağaçtaki ana işaretçiler gibi daha yaygın durumlarda yardımcı olurlar. İyi bir geçici çözüm, tüm grafiğe referansı temsil eden ve imhayı yöneten bir bağlam nesnesini korumaktır. Yeniden ifade etmek bir pazarlamacı değildir, ancak programcının kısıtlamalarının farkında olmasını gerektirir. Yani GC'den biraz daha yüksek bilişsel maliyete sahiptir.
amon

1
Ref saymanın nadiren kullanılmasının önemli bir nedeni, sadeliğine rağmen genellikle GC'den daha yavaş olmasıdır.
Rufflewind

14

Bence çöp toplama işleminin en ikna edici avantajı, besteciliğe izin vermesidir . Bellek yönetiminin doğruluğu çöp toplanan ortamda yerel bir özelliktir. Her bölüme ayrı ayrı bakabilir ve belleğin sızıp sızmayacağını belirleyebilirsiniz. Herhangi bir sayıda hafızaya uygun parçayı birleştirin; doğru kalırlar.

Referans saymaya güveniyorsanız, bu özelliği kaybedersiniz. Başvurunuzun bellek sızıntısı yapıp yapamayacağı, referans sayma ile tüm uygulamanın global bir özelliği haline gelir. Parçalar arasındaki her yeni etkileşim, yanlış mülkiyeti kullanma ve hafıza yönetimini bozma olanağına sahiptir.

Farklı dillerde programların tasarımı üzerinde çok görünür bir etkiye sahiptir. GC dillerinde programlar biraz daha fazla etkileşime sahip nesnelerin çorbaları olma eğilimindeyken, GC'siz dillerde ise aralarında kesin olarak kontrol edilen ve sınırlı etkileşimlere sahip yapısal parçaları tercih etme eğilimindedir.


1
Doğruluk, sadece nesneler referansları sadece kendi adına tutuyorsa ve bu referansların hedefleri adına yapmıyorsa, bir araya getirilebilir. Bildirimler gibi şeyler karışıma girdikten sonra (Bob, Joe'ya atıfta bulunur çünkü Joe, Bob'dan bir şey olduğunda ona bildirmesini istedi ve Bob bunu yapmaya söz verdi, ancak Bob aksi halde Joe'yu önemsemiyor), GC doğruluğu genellikle kapsamlı kaynak yönetimi gerektiriyor [birçok durumda manuel olarak uygulanır, çünkü GC sistemleri C ++ otomasyonundan yoksundur].
supercat

@supercat: "GC doğruluğu genellikle kapsamlı kaynak yönetimi gerektirir". Eh? Kapsam yalnızca kaynak kodunda bulunur ve GC yalnızca çalışma zamanında bulunur (ve bu nedenle, kapsamın varlığına tamamen önem vermez).
Jon Harrop

@JonHarrop: "Kapsam" terimini bir C ++ "işaretçi gösterici" ile aynı anlamda kullanıyordum [nesnenin ömrü, onu tutan kabın ömrü olmalıdır], çünkü orijinal soru tarafından ima edildi. Demek istediğim, olayların yalnızca bir GC sisteminde birleştirilemeyeceği gibi amaçlarla uzun ömürlü referanslar oluşturmasıdır. Doğruluk için, belirli referansların güçlü olması ve belirli referansların zayıf olması ve hangi referansların bir nesnenin nasıl kullanıldığına bağlı olması gereken olması gerekir. Örneğin ...
supercat,

... varsayalım ki Fred ve Barney, belirli bir dizindeki herhangi bir şey değiştirildiğinde, bildirim için kayıt olurlar. Fred'in işleyicisi hiçbir şey yapmaz, ancak değeri istek üzerine rapor edebileceği, ancak başka bir kullanımı olmayan bir sayacı arttırır. Belirli bir dosya değiştirilirse Barney'nin işleyicisi yeni bir pencere açar. Doğruluk için Fred zayıf bir etkinliğe abone olmalı, ancak Barney güçlü olmalı, ancak zamanlayıcı nesnesinin bunu bilmesinin hiçbir yolu olmayacak.
supercat,

@supercat: Doğru. Bunun "sık sık" olduğunu söyleyemem. 30 yıllık programlamada sadece bir kere rastladım.
Jon Harrop

7

Kapaklar hemen hemen tüm modern dillerin vazgeçilmez bir özelliğidir. GC ile uygulama yapmak çok kolaydır ve RAII ile hak kazanmak çok zor (imkansız olmasa da), çünkü temel özelliklerinden biri değişkenlerinizin ömrünü soyutlamanıza izin vermeleridir!

C ++ sadece herkesin ondan 40 yıl sonra onları aldı ve birçok akıllı insan tarafından doğru bir şekilde çalışılması çok çalıştı. Buna karşılık, programlama dillerini tasarlama ve uygulamada sıfır bilgisi olan insanlar tarafından tasarlanan ve uygulanan birçok kodlama dili vardır.


9
C ++ 'daki kapanışların iyi bir örnek olduğunu sanmıyorum . C ++ 11'deki lambdalar, sadece sınıflara göre sınıflandırma yapan (C ++ 11'i önemli ölçüde önceden bildiren) functor sınıfları için sözdizimsel bir şekerdir ve eşit derecede hafıza-güvensizdir: Eğer referansla bir şey yakalarsanız ve o şey öldükten sonra kapatmayı çağırırsanız, basitçe UB'ye sahip olursunuz, bir referansı geçerli olmaktan daha uzun süre tutmak gibi. 40 yıl gecikmiş görünmeleri, kendilerini nasıl güvende tutabileceklerini çözmekten ziyade, FP'nin gecikmiş onaylarından kaynaklanmaktadır. Ve onları tasarlarken kesinlikle çok büyük bir görevdi, bu çabanın çoğunun yaşam boyu dikkate alındığı konusunda şüpheliyim.

Delnan ile aynı fikirdeyim: C ++ kapatılmadı: Onları çağırdığınızda bir çekirdek dökümü almak istemiyorsanız, bunları çok dikkatli bir şekilde programlamanız gerekir.
Giorgio

2
@delnan: referansla yakala lambasının kasıtlı olarak bu [&]sözdizimi vardır. Herhangi bir C ++ programcısı &işareti zaten referanslarla ilişkilendirir ve eski referansları bilir.
MSalters

2
@ MSalters Amacınız nedir? Referans bağlantısını kendim çizdim. C ++ lambdaların son derece güvensiz olduklarını söylemedim, referansları kadar güvensiz olduklarını söyledim. C ++ lambdaların kötü olduğunu iddia etmedim, bu cevabın iddiasına karşı çıktım (C ++ 'ın çok geç kaldığını çünkü nasıl doğru yapılacağını bulmak zorunda kaldıklarını) savundum.

5
  1. SBMM programları daha belirleyici kılar (bir nesnenin ne zaman imha edildiğini tam olarak söyleyebilirsiniz);

Çoğu programcı için işletim sistemi determinist değildir, bellek ayırıcıları determinist değildir ve yazdıkları programların çoğu eşzamanlıdır ve bu nedenle, kendiliğinden determinist değildir. Bir yıkıcıya, kapsamın hemen sonunda, biraz önce veya biraz sonra değil, tam olarak sonunda çağrılan kısıtlamayı eklemek, programcıların büyük çoğunluğu için önemli bir pratik fayda değildir.

  1. GC'yi kullanan dillerde, genellikle GC'nin amacını kısmen yenen ve aynı zamanda hataya açık olan manuel kaynak yönetimi (örneğin, Java'daki dosyaları kapatma) yapmanız gerekir;

usingC # ve useF # bölümüne bakınız .

  1. Yığın hafızası ayrıca (çok zarif bir şekilde imo) kapsamına bağlı olabilir (C ++ 'daki std :: shared_ptr dosyasına bakın).

Başka bir deyişle, genel amaçlı bir çözüm olan yığını alabilir ve yalnızca ciddi şekilde sınırlayıcı olan belirli bir durumda çalışmak üzere değiştirebilirsiniz. Bu elbette doğrudur, ancak işe yaramaz.

SBMM neden daha yaygın kullanılmıyor? Dezavantajları nelerdir?

SBMM yapabileceklerinizi sınırlandırır:

  1. SBMM birinci sınıf sözlüksel kapanışlarla yukarı yönlü sorun yaratır, bu nedenle kapakların popüler olması ve C # gibi dillerde kullanımı kolaydır, ancak C ++ 'ta nadir ve zor. Programlamada işlevsel yapıların kullanımına yönelik genel bir eğilim olduğuna dikkat edin.

  2. SBMM yıkıcılar gerektirir ve bir fonksiyonun dönebilmesi için daha fazla iş ekleyerek kuyruk çağrılarını engeller. Kuyruk çağrıları, genişletilebilir durum makineleri için faydalıdır ve .NET gibi şeyler tarafından sağlanır.

  3. Bazı veri yapılarının ve algoritmalarının SBMM kullanarak gerçekleştirilmesi oldukça zordur. Temel olarak, döngülerin doğal olarak gerçekleştiği her yerde. En belirgin şekilde grafik algoritmaları. Kendi GC'nizi yazıyorsunuz.

  4. Eşzamanlı programlama daha zordur çünkü kontrol akışı ve bu nedenle, nesne yaşamları burada doğal olarak deterministik değildir. Mesaj geçiş sistemlerinde pratik çözümler, mesajların derin kopyalanması ve aşırı uzun ömürlerin kullanılması eğilimindedir.

  5. SBMM, kaynak kodunda kapsamlarının sonuna kadar, gerekenden daha uzun ve gerekenden daha uzun olabilen nesneleri canlı tutar. Bu, yüzen çöp miktarını arttırır (geri dönüştürülmeyi bekleyen ulaşılamaz nesneler). Buna karşılık, çöp toplama izleri, nesnelere yapılan son referansın kaybolmasından kısa bir süre sonra serbest nesnelere yönelir. Bkz. Bellek yönetimi mitleri: hızlılık .

SBMM öylesine sınırlayıcı ki, programcıların ömür boyu yuvalanamayan durumlar için kaçış yoluna ihtiyacı var. C ++ 'da shared_ptrbir kaçış yolu sunar, ancak çöp toplanmasından yaklaşık 10 kat daha yavaş olabilir . Bu yüzden GC yerine SBMM kullanmak çoğu insanı çoğu zaman yanlış adımlara sokar. Ancak, bunun faydasız olduğu söylenemez. SBMM, kaynakların sınırlı olduğu sistemler ve gömülü programlama bağlamında hala değerlidir.

FWIW, Forth ve Ada'ya göz atmak ve Nicolas Wirth'in çalışmalarını okumak isteyebilirsiniz.


1
Hangi bitleri söylerseniz makaleleri detaylandırabilir veya alıntı yapabilirim.
Jon Harrop,

2
Bazı nadir kullanım durumlarında, tüm kullanım durumlarında her yerde hazır bulunma yerine 10 kat daha yavaş olmak ne kadar anlamlı? C ++ unique_ptr'a sahiptir ve çoğu amaç için yeterlidir. Bunun yanında, RAII yalak C ++ 'a (arkaik bir dil olmaktan nefret etmeyi sevdiği bir dil) saldırmak yerine, RAII yalak bir dile saldırmak için saldıracaksanız, örneğin RAII ailesinin küçük bir kardeşini deneyiniz. Rust temel olarak, C ++ 'ın yanlış yaptığı her şeyi doğru yaparken C ++' ın doğru yaptığı her şeyi doğru yapar. Ayrıca 'kullanmak' size çok sınırlı sayıda kullanım senaryoları verir ve kompozisyonu yok sayar.
user1703394

2
"Birkaç nadir kullanım durumunda, tüm kullanım durumlarında her yerde hazır bulunma yerine 10 kat daha yavaş olmak ne kadar uygun?" İlk olarak, bu dairesel bir argümandır: shared_ptrC ++ 'ta nadiren görülür çünkü çok yavaştır. İkincisi, bu bir elma ve portakal karşılaştırmasıdır (daha önce bahsettiğim makalenin gösterdiği gibi) çünkü shared_ptrbir üretim GC'sinden birçok kez daha yavaştır. Üçüncüsü, GC'ler her yerde bulunmaz ve LMax ve Rapid Addition'ın FIX motoru gibi yazılımlarda kullanılmaz.
Jon Harrop,

1
@Jon Harrop, lütfen beni aydınlatmazsanız. Derin kaynak kullanmanın geçişli etkilerini azaltmak için hangi büyü tarifini 30 + yıl boyunca kullandınız? 30 yılı aşkın bir süredir böyle sihirli bir tarif olmadan, sadece onun tarafından başka nedenlere ısırılmış olmanız gerektiği sonucuna vardım.
user1703394

1
@Jon Harrop, paylaşılan_ptr nadir değildir, çünkü yavaş, iyi tasarlanmış bir sistemde 'paylaşılan sahiplik' ihtiyacının nadir olması nedeniyle nadirdir.
user1703394,

4

Tabii ki TIOBE gibi bazı popülerlik endekslerine bakıldığında, elbette, ama sanırım bu tür bir soru sormak için bunu kullanmak doğru olur), ilk önce ilk 20'nin ~% 50'sinin "betik dilleri" veya "SQL lehçeleri" olduğunu görüyorsunuz. "Kullanım kolaylığı" ve soyutlama araçlarının deterministik davranıştan çok daha fazla öneme sahip olduğu ". Kalan "derlenmiş" dillerden, SBMM'li dillerin yaklaşık% 50'si ve ~% 50'siz dilleri vardır. Bu nedenle, kodlama dillerini hesaplamalarınızdan çıkarırken, varsayımınızın yanlış olduğunu, derlenmiş diller arasında SBMM'li olanların olmayanlar kadar popüler olduğunu söyleyebilirim.


1
“Kullanım kolaylığı” nın determinizmden farkı nedir? Deterministik bir dil, deterministik olmayan bir dilden daha kolay kullanılmalı mıdır?
Philipp

2
@Philipp Yalnızca belirleyici olan veya gerçekte önemli olmayan şeylerden biri. Nesne yaşam süresi kendi başına önemli değildir (C ++ ve arkadaşlar, yaşam saatine itiraz eden önemli şeyleri bağlarlar, çünkü yapabilirler). Bir zaman ulaşılamaz nesne arındırılır tanımı gereği, daha fazla kullanmadığınızda, çünkü önemli değil.

Bir şey daha, Perl ve Python gibi çeşitli "betik dilleri" de bellek yönetimi için referans sayımı olarak kullanılır.
Philipp

1
@Philipp En azından Python dünyasında, bu bir dilin özelliği değil CPython'un bir uygulama detayı olarak kabul edilir (ve hemen hemen her uygulamada sayılmaya devam eder). Dahası, GC yedekleme döngüsüyle sayılan tümünü seçmeden bırakma referansının SBMM veya RAII olarak nitelendirilmediğini iddia edeceğim. Aslında, bu bellek yönetimi tarzını RAII ile karşılaştırılabilir olarak kabul eden RAII destekçilerini bulmakta zorlanacaksınız (çoğunlukla olmadığı için, herhangi bir yerdeki döngüler programın herhangi bir yerinde derhal tahsis edilmesini önleyebilir).

3

Henüz kimsenin bahsetmediği bir GC sisteminin en büyük avantajı, bir GC sistemindeki bir referansın, var olduğu sürece kimliğini koruyacağının garanti edilmesidir . Referansın kopyaları varken bir nesne IDisposable.Dispose(.NET) veya AutoCloseable.Close(Java) çağırırsa , bu kopyalar aynı nesneye başvurmaya devam eder. Nesne artık hiçbir şey için faydalı olmayacak, ancak onu kullanma girişimleri, nesnenin kendisi tarafından kontrol edilebilen öngörülebilir davranışa sahip olacak. Buna karşılık, C ++ 'da, deletebir nesne kod çağırır ve daha sonra onu kullanmaya çalışırsa, sistemin tüm durumu tamamen tanımsız hale gelir.

Unutulmaması gereken bir diğer önemli husus, kapsam tabanlı hafıza yönetiminin açıkça tanımlanmış mülkiyeti olan nesneler için çok iyi çalışmasıdır. Tanımlanmış mülkiyeti olmayan nesnelerle çok daha az işe yarar ve bazen düpedüz düpedüz çalışır. Genelde, değişken nesnelerin sahiplerine sahip olması gerekir; değişmez nesnelere ihtiyaç duymaz, ancak bir kırışıklık vardır: herhangi bir referansa maruz kalmayacağından emin olarak, kodun değişmez verileri tutmak için değiştirilebilen türlerin bir örneğini kullanması çok yaygındır. örneği değiştirebilecek kod. Böyle bir senaryoda, değişken sınıf örnekleri birden fazla değişmez nesne arasında paylaşılabilir ve dolayısıyla net bir mülkiyeti yoktur.


4
İlk yarıda bahsettiğiniz özellik hafıza güvenliğidir. Bir GC hafıza güvenliğini sağlamak için çok kolay bir yol olsa da, bir GC gerekli değildir: İyi hazırlanmış bir örnek için Rust'a bakın.

@delnan: rust-lang.org’u görüntülediğimde tarayıcım oradan yararlı bir şekilde geçemiyor; daha fazla bilgiyi nerede aramalıyım? Benim izlenimim, GC'siz bellek güvenliğinin, bir uygulamanın yapması gerekebilecek her şeye uymayabilecek veri yapılarına belirli kısıtlamalar getirdiği, ancak yanlış olduğunu kanıtlamaktan mutluluk duyacağım.
supercat

1
Bunun için tek bir (ya da hatta küçük) iyi referanslar setini bilmiyorum; Rust bilgim bir ya da iki yıl boyunca e-posta listesini (ve çeşitli dersler, dil tasarımı blog yazıları, github sorunları, ThisWeekInRust ve daha fazlası dahil olmak üzere, postalarla bağlantılı olan her şeyi) okumaya başladı. İzlenimlerinize kısaca değinmek için: Evet, her güvenli yapı (zorunlu olarak) kısıtlamalar getirir, ancak hemen hemen her hafıza güvenli kod parçası için uygun bir güvenli yapı vardır veya yazılabilir. En yaygın olanları zaten dilde ve stdlib'de bulunmaktadır, diğerleri ise kullanıcı koduyla yazılabilir.

@delnan: Rust, referans sayaçlarında kilitli güncellemeler gerektiriyor mu, yoksa kesin bir mülkiyeti olmayan değişmez nesnelerle (veya değişmez şekilde sarılmış değiştirilebilir nesnelerle) başa çıkmanın başka yolları da var mı? Rust, hem "nesneye sahip olma" hem de "sahip olmayan" işaretçiler kavramına sahip mi? “Sahipleri” olan tek bir referansı olan nesnelerin ve onların olmayan diğer referansların fikrini tartışan “Xonor” işaretçilerine dair bir yazıyı hatırlıyorum; "sahip olma" referansı kapsam dışına çıktığında, sahip olmayan tüm referanslar ölü nesnelere referans olur ve bu şekilde tanımlanabilir ...
supercat

1
Stack Exchange yorumlarının bir dille tur atmak için doğru araç olduğunu sanmıyorum. Eğer hala ilgileniyorsanız, doğrudan kaynağa gidebilirsiniz (#rust IRC, rust-dev posta listesi, vb.) Ve / veya bir sohbet odasından bana ulaşabilir (bir tane oluşturabilirsiniz).

-2

Öncelikle, RAII'nin SBMM'ye eşit olduğunu anlamak çok önemlidir. veya hatta SBRM'ye. RAII'nin en önemli (ve en az bilinen ya da en çok takdir edilen) niteliklerinden biri, 'kaynak olmayı' kompozisyona geçiş niteliğinde olmayan bir özellik haline getirmesidir.

Aşağıdaki blog yazısı, RAII'nin bu önemli yönünü tartışmakta ve deterministik olmayan GC kullanan GCed dillerinde kaynak yönetimi ile karşılaştırmaktadır.

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

RAII'nin çoğunlukla C ++ 'da kullanılmasına rağmen, Python (sonunda VM tabanlı olmayan sürüm) RAII'nin GC ile birlikte kullanılmasına olanak veren yıkıcı ve deterministik GC'ye sahip olduğunu not etmek önemlidir. Öyle olsaydı iki dünyanın da en iyisi.


1
-1 Bu okuduğum en kötü yazılardan biri.
Jon Harrop

1
Dillerdeki sorun onların GC'yi desteklemesi değil, RAII'yi terk etmeleridir. Bir dilin / çerçevenin her ikisini de destekleyememesi için hiçbir neden yoktur.
supercat,

1
@Jon Harrop, detaylı bilgi verebilir misiniz? Makalede yer alan iddialardan, iddia edilmeyen ilk 3 iddiadan biri bile var mı? Verimlilik talebine katılmayacağınızı düşünüyorum, ancak diğer 3 iddia kesinlikle geçerli. En önemlisi, kaynak olmanın geçişliliği ile ilgili ilk olanı.
kullanıcı1703394

2
@ user1703394: Öncelikle, makalenin tamamı çöp toplama "GCed dili" çevresine dayanıyor, aslında çöp toplama ile ilgisi yok. İkincisi, aslında hatalar nesne yönelimli programlama ile yatarsa ​​çöp toplanmasından sorumlu tutulur. Sonunda, argümanı 10 yıl geç kaldı. Programcıların büyük çoğunluğu çoktan daha fazla üretkenlik sundukları için toplanan dilleri zaten modernize ediyorlardı.
Jon Harrop,

1
Somut örnekleri (RAM, açık dosya kolları, kilitler, iplikler) oldukça açıklayıcı. Bunlardan herhangi biriyle doğrudan ilgili kodları yazmak zorunda kaldığımı en son hatırlamam konusunda zorlanıyorum. RAM ile GC her şeyi otomatikleştirir. Dosya tanıtıcıları ile File.ReadLines file |> Seq.length, soyutlamaların benim için kapandığı gibi bir kod yazarım . .NET Taskve F # ile değiştirdiğim kilitler ve iş parçacıkları MailboxProcessor. Bu "Manuel kaynak yönetimi miktarını patlattık" tamamen saçmalık.
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.