Akıllı işaretçiler varsa neden Çöp Toplama


67

Bugünlerde pek çok dil çöp toplanıyor. C ++ için üçüncü şahıslar tarafından bile kullanılabilir. Fakat C ++ 'da RAII ve akıllı işaretçiler var. Peki, çöp toplama kullanmanın amacı nedir? Fazladan bir şey mi yapıyor?

Ve C # gibi diğer dillerde, eğer tüm referanslar akıllı işaretçiler olarak kabul edilirse (RAII'yi bir yana tutarak), şartnameye ve uygulamaya göre, çöp toplayıcılarına ihtiyaç duyulacak mı? Hayır ise, o zaman neden böyle değil?


1
Bu soruyu sorduktan sonra anladığım bir şey var: Akıllı işaretçilerin otomatik olarak serbest bırakmayı yönetmesi için RAII'ye ihtiyacı var .
Gulshan

8
Akıllı işaretçiler, GC için RAII kullanmak anlamına gelir;)
Dario

Heh, c # RAII ile birlikte tüm "çöp toplama" işlemlerinde bir seçeneğe sahip olmalıdır. Uygulama kapatılırken dairesel referanslar tespit edilebilir, tek ihtiyacımız olan, Program.cs sınıfı ayrılmadan sonra hangi ayırmaların hala bellekte olduğunu görmektir. Ardından dairesel referanslar, bir tür haftalık referanslarla değiştirilebilir.
AareP

Yanıtlar:


67

Peki, çöp toplama kullanmanın amacı nedir?

Referansın akıllı işaretçiler sayılmasını kastettiğimi ve onların (ilkel) bir çöp toplama biçimi olduğunu not edeceğim, bu yüzden "referanslı sayılan akıllı işaretçilere göre diğer çöp toplama biçimlerinin avantajları nelerdir" sorusuna cevap vereceğim. yerine.

  • Doğruluk . Referans sayma tek başına döngüleri sızdırıyor, böylece referans sayılan akıllı işaretçiler, döngüleri yakalamak için başka teknikler eklenmediği sürece genel olarak belleği sızdırıyor. Bu teknikler eklendiğinde referans sayımının basitlik avantajı ortadan kalktı. Ayrıca, kapsam tabanlı referans sayma ve izleme GC'lerinin farklı zamanlarda değerler topladığını, bazen referans sayımının daha erken toplandığını ve bazen izleme GC'lerinin daha önce toplandığını unutmayın.

  • Üretilen . Akıllı işaretçiler, özellikle referans sayıları atomik olarak çarptığında, çok iş parçacıklı uygulamalar bağlamında, en az verimli çöp toplama biçimlerinden biridir. Bunu hafifletmek için tasarlanmış gelişmiş referans sayma teknikleri vardır, ancak GC'lerin izlenmesi hala üretim ortamlarında tercih edilen bir algoritmadır.

  • Gecikme . Tipik akıllı işaretçi uygulamaları yıkıcıların çığlaşmasına izin vererek sınırsız duraklama sürelerine neden olur. Diğer çöp toplama biçimleri çok daha fazla artımlıdır ve gerçek zamanlı bile olabilir, örneğin Baker'ın koşu bandı.


23
Bu cevabın en iyi cevabı bulduğuna inanamıyorum. C ++ akıllı işaretçilerinin tam olarak anlaşılmadığını gösterir ve basitçe gülünç olduğu gerçeğiyle çok fazla uyuşmayan iddialarda bulunur. İlk olarak, C ++ kodunun iyi tasarlanmış bir parçasında en baskın olan akıllı gösterici, paylaşım göstericisi değil, benzersiz göstericidir. en.cppreference.com/w/cpp/memory/unique_ptr İkincisi, akıllı göstericiler üzerinden deterministik olmayan çöp toplamanın 'performans' avantajlarını ve gerçek zamanlı avantajlarını iddia ettiğinize inanamıyorum.
user1703394

4
@ user1703394 Görünüşe göre cevap veren akılda bulunan işaretçileri akılda tuttu (haklı ya da yanlış, OP'lerin ne önerdiğinden emin değilim), belirleyici olmayan çöp toplamadan daha az performans gösteriyor.
Nathan Cooper

8
Bunların hepsi saman adam argümanlarıdır ve yalnızca gerçek soruyu tamamen görmezden gelirseniz veya farklı akıllı işaretçilerin gerçek kullanım modellerini yok sayarsanız geçerlidir. Soru akıllı işaretçilerle ilgiliydi. Evet shared_ptr akıllı bir işaretçi ve evet shared_ptr akıllı işaretçilerin en pahalısıdır, ancak hayır, herhangi bir performans argümanına yakın olan herhangi bir yerde yaygın kullanımları için gerçek bir argüman yoktur. Cidden, bu cevap referans sayma konusunda bir soruya taşınmalıdır. İyi bir akıllı işaretçi sorusuna cevap veren kötü bir referans.
user1703394

4
"Akıllı işaretçiler daha geniş bir kavram değildir", Cidden mi? Bu ifadenin, şu ana kadar yaptığınız tüm geçerli argümanları ne kadar zayıflattığını bilmiyorsunuz. Belki Rust'a bir göz atın ve anlambilimdekileri taşıyın: koerbitz.me/posts/… Hafıza modelinde C ++ 11 / C ++ 14'ün akıllıca geliştirdiği gerçeğini kaçırması C ++ ile tarihi deneyime sahip kişiler için kolaydır işaretçiler ve hareket anlambilimi, öncekilerden tamamen farklı bir canavardır. Rust'un nasıl yaptığını inceleyin, C ++ 'dan daha temiz ve taze bir bakış açısı sunuyor.
user1703394

6
@ user1703394: "Yıkıcılar nedeniyle sınırlandırılmamış duraklamalar, RAII'nin bellek dışı kaynaklar için kullanılmasının talihsiz bir özelliğidir". Hayır, bunun bellek dışı kaynaklarla hiçbir ilgisi yok.
Jon Harrop

63

Kimse bu açıdan bakmadığından sorunuzu tekrar ifade edeceğim: bir kütüphanede yapabiliyorsanız neden dile bir şeyler koymalısınız? Özel uygulama ve sözdizimsel ayrıntıları göz ardı ederek, GC / akıllı işaretçiler temelde bu sorunun özel bir durumudur. Bir kitaplıkta uygulayabiliyorsanız neden bir çöp toplayıcıyı dilin kendisinde tanımlayın?

Bu sorunun birkaç cevabı var. İlk önce en önemlisi:

  1. Tüm kodların birlikte çalışabilmesi için kullanabildiğinden emin olun. Bu, kod kullanımının ve kod paylaşımının Java / C # / Python / Ruby'ye kadar gerçekten neden olmadığının büyük nedeni. Kütüphanelerin iletişim kurması gerekir ve sahip oldukları tek güvenilir ortak dil, dilin özelinde (ve bir dereceye kadar standart kütüphanesinde) olan şeydir. C ++ 'ta kütüphaneleri tekrar kullanmayı denediyseniz, muhtemelen standart bir bellek semantiğinin neden olmadığı korkunç bir acı ile karşılaştınız. Bir librayı bir yapıya geçirmek istiyorum. Bir referansı iletir miyim? Işaretçi? scoped_ptr?smart_ptr? Sahipliği mi geçiyorum, değil mi? Bunu belirtmenin bir yolu var mı? Ya lib'in tahsis etmesi gerekiyorsa? Tahsisatçı vermek zorunda mıyım? C ++, dilin bellek yönetimini yapmamakla birlikte, her bir kütüphane çiftini burada kendi özel stratejilerini müzakere etmek zorunda bırakıyor ve hepsinin de aynı fikirde olmasını sağlamak gerçekten zor. GC bunu eksiksiz bir sorun haline getiriyor.

  2. Etrafında sözdizimini tasarlayabilirsiniz. C ++ bellek yönetimini kapsamadığı için, kullanıcı düzeyinde kodun tüm ayrıntıları ifade etmesini sağlamak için bir dizi sözdizimsel kanca sağlaması gerekir. İşaretçileriniz, referanslarınız, constreferans düzenleme operatörleriniz, dolaysız operatörleriniz, adres bilgileriniz vb. Var . Bellek yönetimini dilin içine alırsanız, sözdizimi bunun etrafında tasarlanabilir. Bu operatörlerin tümü kayboluyor ve dil daha temiz ve basitleşiyor.

  3. Yatırımın yüksek getiri elde edersiniz. Herhangi bir kod parçasının ürettiği değer, onu kullanan kişi sayısı ile çarpılır. Bu, ne kadar çok kullanıcınız varsa, bir yazılım parçası için harcayabileceğiniz kadar para harcarsınız. Bir özelliği dile taşıdığınızda, dilin tüm kullanıcıları onu kullanır. Bu, yalnızca bu kullanıcıların bir alt kümesi tarafından kullanılan bir kütüphaneye verebileceğinizden daha fazla çaba harcayabileceğiniz anlamına gelir. Java ve C # gibi dillerin kesinlikle birinci sınıf VM'lere ve fevkalade yüksek kaliteli çöp toplayıcılarına sahip olmalarının nedeni budur: bunları geliştirmenin maliyeti milyonlarca kullanıcı arasında itfa edilmektedir.


Harika cevap! Keşke birden fazla defa oy kullanabilseydim ...
Dean Harding,

10
Çöp toplama işleminin aslında C # dilinde değil , .NET Framework'te , özellikle de Ortak Dil Çalışma Zamanı'nda (CLR) uygulandığını belirtmekte fayda var .
Robert Harvey,

6
@RobertHarvey: Dil tarafından uygulanmadı , ancak dilin işbirliği olmadan çalışmaz . Örneğin, derleyici, sabitlenmemiş bir nesneye referans tutan her yazıcının ya da yığın çerçeve kaymasının konumunu belirten bilgileri içermelidir. Bu, dil desteği olmadan desteklenemeyen, istisnasız ve istisnasız bir durumdur .
supercat

GC'nin dili ve gerekli çerçeveyi desteklemesinin en büyük avantajı, başka bir amaç için tahsis edilebilecek belleğe hiçbir referansın bulunmamasını temin etmesidir. Biri Disposebir bitmap'i içine alan bir nesneyi çağırırsa , o nesneye yapılan herhangi bir başvuru, atılan bir bitmap nesnesine bir başvuru olacaktır. Nesne, diğer kodun hala onu kullanmayı beklerken, önceden silinmişse, bitmap sınıfı, diğer kodun öngörülebilir şekilde başarısız olmasını sağlayabilir . Buna karşılık, serbest belleğe bir başvuru kullanmak Tanımsız Davranış.
supercat

34

Çöp toplama temelde, yalnızca tahsis edilen nesneler, artık erişilemez olduktan sonra bir noktada otomatik olarak serbest bırakılması anlamına gelir.

Daha doğru bir şekilde, programa erişilemez hale geldiklerinde serbest bırakılırlar , çünkü dairesel referanslı nesneler aksi takdirde asla serbest bırakılmaz.

Akıllı işaretçiler sadece sıradan bir işaretçi gibi davranan herhangi bir yapıya atıfta bulunur, ancak bazı ekstra işlevler de eklenmiştir. Bunlar arasında , tahsisat sayılması ile sınırlı olmamakla birlikte, aynı zamanda yazma-kopyalama, cilt kontrolleri ...

Şimdi, belirttiğiniz gibi, akıllı işaretçiler bir çöp toplama biçimini uygulamak için kullanılabilir .

Ancak düşünce treni şu şekilde gider:

  1. Çöp toplama işlemi, uygun olduğu için güzel bir şey ve daha az şeyle ilgilenmem gerekiyor
  2. Bu nedenle: Dilimde çöp toplama istiyorum
  3. Şimdi GC'yi dilime nasıl sokabilirim?

Tabii ki, baştan böyle tasarlayabilirsiniz. C # çöp toplanacak şekilde tasarlandı , bu nedenle sadece newnesneniz ve referanslar kapsam dışına çıktığında serbest bırakılacak. Bunun nasıl yapılacağı derleyiciye bağlıdır.

Ancak C ++ 'da çöp toplama amaçlı değildi. Eğer bir işaretçi tahsis edersek int* p = new int;ve kapsam dışına çıkarsak, pkendisi yığından çıkarılır, fakat kimse tahsis edilen hafızaya bakmaz.

Şimdi baştan sahip olduğunuz tek şey deterministik yıkıcılar . Bir nesne, yaratıldığı kapsamı terk ettiğinde, yıkıcısı olarak adlandırılır. Şablonlar ve operatörün aşırı yüklenmesi ile birlikte, işaretçi gibi davranan bir sarmalayıcı nesnesi tasarlayabilirsiniz, ancak ona bağlı kaynakları temizlemek için yıkıcı işlevini kullanır (RAII). Buna akıllı işaretçi diyorsun .

Bunların hepsi C ++ 'a özgüdür: Operatör aşırı yüklenmesi, şablonlar, yıkıcılar, ... Bu özel dil durumunda, size istediğiniz GC'yi sağlamak için akıllı işaretçiler geliştirdiniz.

Ancak, GC ile baştan bir dil tasarlarsanız, bu sadece bir uygulama detayıdır. Diyelim ki nesne temizlenecek ve derleyici bunu sizin için yapacak.

C ++ gibi akıllı işaretçiler muhtemelen C # gibi dillerde kesin bir tahribata sahip olmayan dillerde bile mümkün olmaz (C # .Dispose()belirli nesnelere çağrı yapmak için sözdizimsel şeker sağlayarak çalışır ). Kayıtsız olmayan kaynaklar sonunda GC tarafından geri alınacak, ancak bunun tam olarak ne zaman gerçekleşeceği belirsiz.

Bu da GC'nin çalışmalarını daha verimli yapmasına izin verebilir. Dilin üzerine, üzerine yerleştirilmiş akıllı işaretçilerden daha derine yerleştirilen .NET GC, örneğin bellek işlemlerini geciktirebilir ve daha ucuz hale getirmek için bloklar halinde gerçekleştirebilir veya nesnelerin ne kadar sıklıkla nesnelere bağlı olarak verimliliği arttırmak için hafızayı hareket ettirebileceklerini bile yapabilir. erişilir.


C # IDisposableve yoluyla deterministik bir yıkım şekline sahiptir using. Ancak, biraz programcı çabası gerektirir, bu nedenle genellikle yalnızca veritabanı bağlantı tanıtıcıları gibi çok kıt kaynaklar için kullanılır.
JSB ձոգչ

7
@JSBangs: Kesinlikle. C ++, GC'yi almak için RAII etrafında akıllı işaretçiler oluştururken, C # diğer yoldan gider ve RAII'yi elde etmek için GC'nin etrafında "akıllı atıklar" oluşturur;) Aslında, RAII'nin C # için istisnai durum için çok zor olması utanç vericidir. güvenli kaynak kullanımı. Örneğin F # daha basit çalışır IDisposablesadece konvansiyonel değiştirerek sözdizimi let ident = valuetarafından use ident = value...
Dario

@Dario: "C # diğer tarafa gider ve RAII almak için GC'nin etrafına" akıllı el atıcılar "oluşturur." C # ile RAII ' usingnin hiçbir şekilde çöp toplama ile ilgisi yoktur, sadece bir değişken C ++' daki yıkıcılar gibi kapsam dışına çıktığında bir fonksiyon çağırır.
Jon Harrop

1
@Jon Harrop: Lütfen ne? Alıntı yaptığınız ifade, herhangi bir referans sayma / akıllı işaretçi / çöp toplama işlemine gerek kalmadan, düz C ++ yıkıcıları hakkındadır.
Dario,

1
“Çöp toplama temelde, yalnızca ayrılan nesneleriniz, artık başvuruda bulunulmadıklarında otomatik olarak serbest bırakılması anlamına gelir. Daha doğru bir şekilde, döngüsel olarak başvurulan nesneler başka türlü serbest bırakılmayacağından, program için erişilemez hale geldiklerinde serbest bırakılırlar.” ... Daha doğrusu , bir zaman sonra değil, bir noktada otomatik olarak serbest bırakıldığını söylemek olacaktır . Not zaman aslında ıslah genellikle yol üstü gerçekleştiğinde ıslah, hemen gerçekleşir anlamına gelir.
Todd Lehman

4

Aklımda, çöp toplama ve bellek yönetimi için kullanılan akıllı işaretçiler arasında iki büyük fark var:

  1. Akıllı işaretçiler döngüsel çöp toplayamaz; çöp toplama olabilir
  2. Akıllı işaretçiler, tüm işlerini başvuru iş parçacığında referans alma, başvuru dışı bırakma ve serbest bırakma anlarında gerçekleştirir; çöp toplama ihtiyacı

Eski, GC'nin akıllı işaretçilerin toplamayacağı çöpleri toplayacağı anlamına gelir; Akıllı işaretçiler kullanıyorsanız, bu tür çöpleri oluşturmaktan kaçınmanız veya el ile uğraşmaya hazır olmanız gerekir.

İkincisi, akıllı işaretçiler ne kadar akıllı olursa olsun, işlemlerinin programınızdaki çalışma ipuçlarını yavaşlatacağı anlamına gelir. Çöp toplama işi erteleyebilir ve diğer iş parçacıklarına taşıyabilir; Genel olarak daha verimli olmasına olanak tanıyan (gerçekten de, modern bir GC'nin çalışma zamanı maliyeti, akıllı işaretçilerin ek yükü olmasa bile normal bir malloc / ücretsiz sistemden daha azdır) uygulama konularının yolu.

Şimdi, akıllı işaretçilerin, programatik yapılar olarak, başka türlü ilginç şeyler yapmak için kullanılabileceğine dikkat edin - Dario'nun cevabını - çöp toplama kapsamı dışında kalan - bakın. Bunları yapmak istiyorsan, akıllı işaretçilere ihtiyacın olacak.

Ancak, bellek yönetimi amacıyla, çöp toplama işleminin yerini alacak akıllı işaretçilerle ilgili bir ihtimal görmüyorum. Onlar sadece bu konuda iyi değiller.


6
@ Tom: Akıllı işaretçilerle ilgili ayrıntılar için Dario'nun cevabına bir göz atın. Akıllı işaretçilerin avantajlarına gelince - deterministik serbest bırakma, kaynakları kontrol etmek için kullanıldığında çok büyük bir avantaj olabilir (yalnızca belleği değil). Aslında, bu öylesine önemli olduğunu kanıtladı: Microsoft usingbloğu C # 'nın sonraki sürümlerinde tanıttı . Dahası, GC'lerin özgün olmayan davranışları gerçek zamanlı sistemlerde (GC'lerin orada kullanılmamasının nedeni) yasaklayıcı olabilir. Ayrıca, GC'lerin, hakikaten bellek sızdıran ve oldukça yetersiz (örneğin, Boehm…) doğru olacak kadar karmaşık olduğunu da unutmayalım .
Konrad Rudolph

6
GC'lerin tanımlanamazlığı bence biraz kırmızı ringa balığıdır - masaüstünde ve sunucu VM'lerde gördükleriniz olmasa da, gerçek zamanlı kullanım için uygun GC sistemleri vardır (IBM'in Recycler'i gibi). Ayrıca, akıllı işaretçiler kullanmak, malloc / free kullanma anlamına gelir ve malloc'un geleneksel uygulamaları, serbest listeyi arama gereği nedeniyle özgün değildir. Hareketli GC sistemleri var daha olsa elbette daha az deterministik miktarda kaldırma süreleri, malloc / ücretsiz sistemlere göre deterministik tahsisi kez.
Tom Anderson,

3
Karmaşıklığa gelince: Evet, GC'ler karmaşıktır, ancak “en çok hafıza sızdırıyor ve oldukça yetersiz” olduğunun farkında değilim ve aksi halde bazı kanıtlar görmekle ilgileneceğim. Boehm delil değildir, çünkü bu çok ilkel bir uygulamadır ve C tipi bir dilde hizmet vermek için inşa edilmiştir. Çok cesur bir çaba ve işe yaraması çok etkileyici, ancak bunu GC'nin bir örneği olarak kabul edemezsiniz.
Tom Anderson,

8
@Jon: kesinlikle saçma sapan değil . bugzilla.novell.com/show_bug.cgi?id=621899 veya daha genel olarak: flyingfrogblog.blogspot.com/2009/01/… Bu iyi bilinen ve tüm muhafazakar GC'lerin bir özelliğidir.
Konrad Rudolph

3
"Modern bir GC'nin çalışma zamanı maliyeti normal malloc / ücretsiz sistemden daha az." Burada kırmızı ringa balığı. Bunun nedeni, geleneksel mallocun korkunç derecede yetersiz bir algoritma olmasıdır. Farklı blok boyutları için çoklu kovalar kullanan modern ayırıcılar tahsis etmek için çok daha hızlıdır, yığın parçalanmasına çok daha az eğilimlidir ve yine de size hızlı bir tahsisat verir.
Mason Wheeler,

3

Çöp toplama terimi, toplanacak çöplerin olduğunu gösterir. C ++ 'da akıllı işaretçiler, en önemlisi unique_ptr olan birden fazla lezzetle gelir. Unique_ptr temel olarak tek bir sahiplik ve kapsam belirleme yapısıdır. İyi tasarlanmış bir kod parçasında çoğu yığın tahsis edilen öğe normalde unique_ptr akıllı işaretçilerinin arkasında bulunur ve bu kaynakların mülkiyeti her zaman iyi tanımlanır. Unique_ptr'de herhangi bir ek yük yoktur ve unique_ptr, insanları geleneksel olarak yönetilen dillere yönlendiren manuel bellek yönetimi sorunlarının çoğunu ortadan kaldırır. Şimdi aynı anda çalışan daha fazla çekirdek daha yaygın hale geliyor ve kodun benzersiz ve iyi tanımlanmış mülkiyeti herhangi bir zamanda kullanmasını sağlayan tasarım ilkeleri performans için daha önemli hale geliyor.

İyi tasarlanmış bir programda, özellikle çok dişli ortamlarda bile, her şey paylaşılan veri yapıları olmadan ifade edilemez ve gerçekten gereken veri yapıları için, iş parçacıklarının iletişim kurması gerekir. C ++ 'daki RAII, tek bir dişli kurulumdaki yaşam boyu endişeler için oldukça iyi çalışır, çoklu dişli kurulumda, nesnelerin ömrü tamamen hiyerarşik olarak tanımlanmış yığın olmayabilir. Bu gibi durumlarda, shared_ptr kullanımı çözümün büyük bir bölümünü sunmaktadır. Bir kaynağın paylaşılan sahipliğini yaratıyorsunuz ve bu C ++ 'da çöp gördüğümüz tek yer, ancak bu kadar küçük miktarlarda, uygun tasarımlı bir c ++ programının' pp 'ile paylaşılan-ptr'ler ile' çöp 'toplama yapmak için daha fazla dikkate alınması gerektiği gibi diğer dillerde uygulanmaktadır. C ++ sadece bu kadar 'çöp' yok

Diğerleri tarafından belirtildiği gibi, referans sayılan akıllı işaretçiler bir çöp toplama biçimidir ve bunun için bir büyük sorun vardır. Çoğunlukla referans sayılan çöp toplama biçimlerinin dezavantajı olarak kullanılan örnek, akıllı işaretçilerle birbirine bağlanmış, birbirlerinin toplanmasını engelleyen nesne kümeleri yaratan yetim veri yapılarının yaratılmasındaki problemdir. Aktör modeline göre tasarlanmış bir programdayken, veri yapıları, çoğunlukla büyük ölçüde kullanılan çoklu dişli programlama için geniş paylaşımlı veri yaklaşımını kullandığınızda, bu tür toplanamaz kümelerin C ++ 'da ortaya çıkmasına izin vermez. endüstrinin bu yetim kümeleri hızla bir gerçeklik haline gelebilir.

Her şeyi özetlemek gerekirse, paylaşılan işaretçi kullanımıyla, çok iş parçacıklı programlama için aktör hesaplama yaklaşımı modeliyle birleştirilmiş unique_ptr'in geniş kullanımı ve paylaşılan_ptr'in sınırlı kullanımının kullanılması anlamına geliyorsa, diğer çöp toplama biçimlerinin herhangi bir satın almadığı anlamına gelmez. ek faydalar. Bununla birlikte, paylaşılan her şey yaklaşımı, her yerde paylaşılan_ptr ile sonuçlanacak olsaydı, eşzamanlılık modellerini değiştirmeyi ya da daha geniş mülkiyet paylaşımına ve veri yapılarına eşzamanlı erişime yönelik daha yönetimli bir dile geçmeyi düşünmelisiniz.


1
RustÇöp toplamaya ihtiyaç duymadığı anlamına mı geliyor ?
Gulshan

1
@ Gülhan Rust, benzersiz benzersiz işaretçileri destekleyen çok az dilden biridir.
KodlarInChaos

2

Akıllı işaretçilerin çoğu referans sayımı kullanılarak uygulanır. Yani, bir nesneye başvuran her akıllı işaretçi, nesnelerin referans sayısını arttırır. Bu sayı sıfıra gittiğinde, nesne serbest bırakılır.

Buradaki problem, dairesel referanslarınız varsa. Yani, A, B'ye bir referansa sahiptir, B, C'ye bir referansa sahiptir ve C, A'ya bir referansa sahiptir. oraya dairesel referansı (örneğin weak_ptr, C ++ kullanarak ) bir "kırın" .

Çöp toplama (tipik olarak) oldukça farklı çalışır. Bugünlerde çoğu çöp toplayıcı bir ulaşılabilirlik testi kullanıyor . Yani, yığıntaki tüm referanslara ve genel olarak erişilebilir olanlara bakar ve sonra bu referansların referans aldığı her bir nesneyi ve başvuruda bulundukları nesneleri vb. İzler. Her şey çöptür.

Bu şekilde, dairesel referanslar artık önemli değil - A, B ve C'ye erişilemediği sürece , bellek geri kazanılabilir.

"Gerçek" çöp toplama işleminin başka avantajları da vardır. Örneğin, bellek tahsisi son derece ucuz: sadece işaretçiyi bellek bloğunun "sonuna" kadar artırın. Ayrılma işleminin de sabit bir itfa edilmiş maliyeti vardır. Ancak elbette C ++ gibi diller, bellek yönetimini istediğiniz şekilde uygulamanıza izin verir, böylece daha da hızlı bir tahsisat stratejisi ortaya koyabilirsiniz.

Tabii ki, C ++ 'da, yığınla ayrılan hafızanın miktarı tipik olarak C # /. NET gibi bir referans-ağır dilden daha azdır. Ancak bu, akıllı işaretçilerle ilgili bir çöp toplama işlemi değil.

Her durumda, mesele kesilmiş ve kuru değildir, biri diğerinden daha iyidir. Her birinin avantajları ve dezavantajları vardır.


2

Yaklaşık var performans . Ayrılmamış bellek, çok fazla yönetim gerektirir. Ayrılmamışlık arka planda çalışıyorsa, ön plan işleminin performansı artar. Maalesef, hafıza tahsisi tembel olamaz (tahsis edilen nesneler kutsal bir anda kutsal bir anda kullanılacaktır), fakat nesneleri serbest bırakabilir.

Büyük bir nesne grubunu ayırmak için C ++ 'ta (GC olmadan) deneyin, "merhaba" yazdırarak silin. Özgür nesnelerin ne kadar sürdüğünü görünce şaşıracaksınız.

Ayrıca, GNU libc belleği ayırmak için daha etkili araçlar sağlar, engelleri görün . Fark etmeliyim, engeller konusunda deneyimim yok, onları hiç kullanmadım.


Prensip olarak bir noktanız var ancak bunun çok basit bir çözümü olan bir mesele olduğu belirtilmelidir: bir tahsisat dağıtmak için bir havuz ayırıcı veya küçük nesne ayırıcı kullanın. Ancak bu kuşkusuz arka planda GC çalıştırmasına göre (biraz) daha fazla çaba gösterir.
Konrad Rudolph

Evet, elbette, GC çok daha rahat. (Özellikle yeni başlayanlar için: mülkiyet problemi yok, hatta silme operatörü bile yok.)
ern0

3
@ ern0: hayır. Akıllı işaretçilerin (referans sayma) bütün noktası, sahiplik sorunu olmadığı ve silme operatörü olmadığıdır.
Konrad Rudolph

3
@Jon: ki, dürüst olmak gerekirse, çoğu zaman. Willy-nilly, farklı dişler arasında nesne durumunu paylaşırsanız, tamamen farklı problemleriniz olacaktır. Birçok insanın bu şekilde program yaptığını itiraf edeceğim, ancak bu yakın zamana kadar var olan kötü iş parçacığı soyutlamalarının bir sonucudur ve çoklu okuma yapmanın iyi bir yolu değildir.
Konrad Rudolph

1
Ayrılma genellikle "arka planda" yapılmaz, aksine tüm ön plan iş parçacıklarını duraklatır. Parti modu çöp toplama işlemi, kullanılmayan alanın konsolide edilmesine izin verdiği için ön plan ipliklerinin duraklamasına rağmen, genellikle bir performans kazancıdır. Biri çöp toplama ve yığın sıkıştırma işlemlerini ayırabilir, ancak - özellikle kulplar yerine doğrudan referanslar kullanan çerçevelerde - her ikisi de "dünyayı durdur" işlemleri olma eğilimindedir ve bunların yapılması genellikle daha pratiktir. birlikte.
supercat,

2

Çöp toplama işlemi daha verimli olabilir - temel olarak bellek yönetiminin ek yükünü 'topluyor' ve hepsini bir kerede yapıyor. Genel olarak, bu işlem, bellek tahsisine daha az genel CPU harcanmasına neden olur, ancak bu, bir noktada büyük bir tahsisat çıkarma aktivitesine sahip olacağınız anlamına gelir. GC uygun şekilde tasarlanmamışsa, bu durum kullanıcı tarafından bir 'duraklama' olarak görülebilirken, GC belleği ayırmaya çalışır. Modern GC'lerin çoğu, en olumsuz koşullar haricinde, kullanıcıyı görünmez kılmak konusunda çok iyidir.

Akıllı işaretçiler (veya herhangi bir referans sayma şeması) tam olarak koda bakmayı beklediğiniz zaman gerçekleşmesi avantajına sahiptir (akıllı işaretçi kapsam dışına çıkarsa, işler silinir). Burada ve orada çok az tahsisat patlaması oluyor. Genel olarak tahsisatta daha fazla CPU zamanı kullanabilirsiniz, ancak programınızdaki her şeye yayıldığından, kullanıcılarınız tarafından görülebilmesi daha az olasıdır (bazı canavar veri yapısının tahsis edilmesinin yasaklanması).

Duyarlılığın önemli olduğu bir şey yapıyorsanız, akıllı işaretçilerin / ref sayımın, olayların tam olarak ne zaman gerçekleştiğini size bildirmesini öneririm, böylece kullanıcılarınız tarafından görülmesi muhtemel olanı kodlarken öğrenebilirsiniz. Bir GC ayarında, çöp toplayıcı üzerinde yalnızca en geçici kontrole sahip olursunuz ve bu şey üzerinde çalışmaya çalışmanız gerekir.

Öte yandan, genel iş hacminiz sizin hedefiniz ise, GC tabanlı bir sistem, bellek yönetimi için gereken kaynakları en aza indirdiği için çok daha iyi bir seçim olabilir.

Döngüleri: Döngü problemini anlamlı biri olarak görmüyorum. Akıllı işaretçilerinizin olduğu bir sistemde, döngüleri olmayan veri yapılarına yönelirsiniz veya bu gibi şeyleri nasıl bıraktığınıza dikkat edin. Gerekirse, otomatik olarak doğru tahribatı garantilemek için sahip olunan nesnelerdeki döngüleri nasıl kıracağını bilen nesneler kullanılabilir. Bazı programlama bölgelerinde bu önemli olabilir, ancak çoğu günlük iş için bu konu dışı.


1
"Bir noktada büyük bir tahsisatsızlık faaliyetine sahip olacaksınız". Fırıncının koşu bandı, güzel bir artımlı çöp toplayıcısının bir örneğidir. memorymanagement.org/glossary/t.html#treadmill
Jon Harrop

1

Akıllı işaretçilerin bir numaralı sınırlaması, dairesel referanslara karşı her zaman yardımcı olmadıklarıdır. Örneğin, A nesnesine sahipseniz, B nesnesine akıllı bir işaretçiyi saklar ve B nesnesi B nesnesine akıllı bir işaretçiyi saklar.

Bunun nedeni, akıllı bir işaretçinin, yukarıdaki senaryoda tetiklenmeyecek belirli bir eylem gerçekleştirmesi gerektiğinden, her iki nesnenin de programa erişilememesidir. Çöp toplama işlemi başa çıkacaktır - nesnelerin program tarafından erişilemediğini ve toplanacaklarını doğru şekilde belirleyecektir.


1

Bu bir spektrum .

Performansa sıkı sıkıya bağlı kalmazsanız ve ezgiyi içine sokmaya hazırsanız, doğru kararları almak ve bunu yapmak için tüm özgürlüğü vermek için üzerinizde tüm onlarla birlikte, montaj veya c'ye son verirsiniz. , tüm bu karışıklık için özgürlük:

"Sana ne yapacağımı söyleyeceğim, sen yap. Güven bana".

Çöp toplama spektrumun diğer ucudur. Çok az kontrole sahipsin, ama seninle ilgilendi:

"Sana ne istediğimi söyleyeceğim, sen olmasını sağla".

Bunun, bir kaynağa artık ihtiyaç duyulmadan tam olarak ne zaman ihtiyaç duyulduğunu bilmek söz konusu olduğunda, güvenilir olmak zorunda olmamanız gibi birçok avantajı vardır, ancak (burada yüzen bazı cevaplara rağmen) performans için iyi değildir ve performansın öngörülebilirliği. (Her şeyde olduğu gibi, size kontrol verildiyse ve aptalca bir şey yaparsanız, daha kötü sonuçlara sahip olabilirsiniz. Ancak derleme zamanında hafızayı boşaltma koşullarının ne olduğunu bilmek, performans kazancı olarak kullanılamaz. saf ötesinde).

RAII, scoping, ref count, vb . Hepsi bu spektrum boyunca ilerlemenize izin vermek için yardımcıdır, ancak hepsi orada değildir. Bütün bunlar hala aktif kullanım gerektiriyor. Yine de, çöp toplamanın yapamayacağı şekilde bellek yönetimi ile etkileşime girmenizi izin veriyor ve talep ediyorlar.


0

Lütfen unutmayın, sonuçta her şey bir CPU çalıştırma talimatına indirgendi. Bildiğim kadarıyla, tüm tüketici sınıfı CPU'lar, verinin bellekte belirli bir yerde saklanmasını gerektiren komut setlerine sahiptir ve söz konusu verilere işaretçileriniz vardır. Temel düzeyde sahip olduğun tek şey bu.

Bunun üzerine her şey çöp toplama, taşınmış olabilecek verilere, yığın sıkıştırma vb. Referanslara atıfta bulunmak, yukarıdaki "adres işaretli bir bellek parçası" paradigması tarafından verilen kısıtlamalar dahilinde çalışmayı yapıyor. Akıllı işaretçilerle aynı şey - STILL, kodu gerçek donanım üzerinde çalıştırmalısınız.

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.