Neden C ++ 'da çöp toplayıcı yok?


270

Her şeyden önce çöp toplamanın yararları yüzünden bu soruyu sormuyorum. Bunu sormamın ana nedeni, Bjarne Stroustrup'un C ++ 'ın bir zamanda bir çöp toplayıcıya sahip olacağını söylediğini bilmem.

Bununla birlikte, neden eklenmedi? C ++ için zaten bazı çöp toplayıcıları var. Bu, "yapılmasından daha kolay" tip şeylerden sadece biri mi? Veya eklenmemiş başka nedenler var mı (C ++ 11'e eklenmeyecek)?

Çapraz bağlantılar:

Sadece açıklığa kavuşturmak için, C ++ 'ın ilk oluşturulduğunda neden bir çöp toplayıcısına sahip olmadıklarını anlıyorum. Kollektörün neden eklenemediğini merak ediyorum.


26
Bu, nefretçilerin her zaman ortaya çıkardığı C ++ ile ilgili ilk on efsaneden biridir. Çöp toplama "yerleşik" değildir, ancak C ++ yapmanın birkaç kolay yolu vardır. Diğerleri zaten aşağıda daha iyi cevap verdi çünkü yorum gönderme :)
davr

5
Ama bu yerleşik olmamakla ilgili bütün mesele, bunu kendiniz yapmak zorundasınız. Tepeden tırnağa okunabilirlik: yerleşik, kütüphane, ev yapımı. C ++ 'ı kendim kullanıyorum ve kesinlikle bir düşmanı değil çünkü dünyadaki en iyi dil. Ancak dinamik bellek yönetimi bir acıdır.
QBziZ

4
@Davr - Ben bir C ++ düşmanı değilim, hatta C ++ 'ın bir çöp toplayıcıya ihtiyacı olduğunu bile iddia etmeye çalışmıyorum. Soruyorum çünkü Bjarne Stroustrup'un ekleneceğini söylediğini ve bunu uygulamanın nedenlerinin ne olduğunu merak ettiğini biliyorum.
Jason Baker

1
Bu makalede Dr. Dobbs'tan C ve C ++ için Boehm Toplayıcı , hem C hem de C ++ ile kullanılabilen açık kaynaklı bir çöp toplayıcıyı açıklamaktadır. C ++ yıkıcıları ve C Standart Kütüphanesi ile bir çöp toplayıcının kullanılmasıyla ortaya çıkan bazı konuları tartışır.
Richard Chambers

1
@rogerdpack: Ama şimdiye kadar o kadar da kullanışlı değil (cevabımı gör ...), bu yüzden olası uygulamaların bir tane için yatırım yapması olası değildir.
einpoklum

Yanıtlar:


160

Örtük çöp toplama eklenmiş olabilir, ancak sadece kesik yapmadı. Muhtemelen sadece uygulama komplikasyonları değil, aynı zamanda insanların genel bir fikir birliğine yeterince hızlı gelememesi nedeniyle.

Bjarne Stroustrup'un kendisinden bir alıntı:

İsteğe bağlı olarak etkinleştirilebilen bir çöp toplayıcının C ++ 0x'in bir parçası olacağını umuyordum, ancak böyle bir toplayıcının dilin geri kalanıyla nasıl bütünleştiğinin ayrıntılı bir belirtimi ile yapmak zorunda olduğum yeterli teknik sorun vardı , sağlanırsa. Esasen tüm C ++ 0x özelliklerinde olduğu gibi, deneysel bir uygulama mevcuttur.

Burada konuyla ilgili iyi bir tartışma var .

Genel Bakış:

C ++ çok güçlü ve neredeyse her şeyi yapmanıza izin veriyor. Bu nedenle performansı etkileyebilecek pek çok şeyi otomatik olarak size aktarmaz. Çöp toplama, akıllı işaretçilerle (referans sayısı 0'a ulaştığında kendilerini otomatik olarak silen işaretçileri bir referans sayısı ile saran nesneler) kolayca uygulanabilir.

C ++, çöp toplama özelliği olmayan rakipler göz önünde bulundurularak oluşturulmuştur. Verimlilik, C ++ 'ın C ve diğerlerine kıyasla eleştiriden kaçınması gereken temel endişeydi.

2 çeşit çöp toplama vardır ...

Açık çöp toplama:

C ++ 0x paylaşılan_ptr ile oluşturulan işaretçiler üzerinden çöp toplama olacaktır

İsterseniz kullanabilirsiniz, istemiyorsanız kullanmak zorunda değilsiniz.

C ++ 0x için beklemek istemiyorsanız şu anda boost: shared_ptr komutunu da kullanabilirsiniz.

Örtülü çöp toplama:

Yine de şeffaf çöp toplama yoktur. Gelecekteki C ++ özellikleri için bir odak noktası olacaktır.

Tr1 neden örtülü çöp koleksiyonuna sahip değil?

C ++ 0x tr1'in sahip olması gereken çok şey var, önceki görüşmelerde Bjarne Stroustrup, tr1'in istediği kadar sahip olmadığını belirtti.


71
Ben ediyorum haline C ++ üzerime çöp toplama zorla eğer bir düşmanı! İnsanlar neden kullanamıyor smart_ptr's? Yolda bir çöp toplayıcı ile düşük seviyeli Unix stili çatallamayı nasıl yaparsınız? Diş çekme gibi diğer şeyler etkilenir. Python'un küresel yorumlayıcı kilidi çoğunlukla çöp toplama nedeniyle vardır (bakınız Cython). C / C ++ 'dan uzak tutun, teşekkürler.
unixman83

26
@ unixman83: Referans sayılan çöp toplama (yani std::shared_ptr) ile ilgili temel sorun, bellek sızıntısına neden olan döngüsel referanslardır. Bu nedenle std::weak_ptr, dağınık olan döngüleri kırmak için dikkatli bir şekilde kullanmalısınız . GC işaretleme ve süpürme stilinde bu sorun yoktur. Diş çekme / çatallama ile çöp toplama arasında doğal bir uyumsuzluk yoktur. Java ve C #, yüksek performanslı önleyici çoklu iş parçacığı ve bir çöp toplayıcıya sahiptir. Çoğu çöp toplayıcının dünyanın çalışmasını durdurması gerektiğinden, gerçek zamanlı uygulamalar ve bir çöp toplayıcıyla ilgili sorunlar vardır.
Andrew Tomazos

9
"Referans sayılan çöp toplama (yani std::shared_ptr) ile ilgili ana sorun döngüsel referanslar" ve ironik olan korkunç performanstır çünkü daha iyi performans genellikle C ++ kullanmanın gerekçesi ... flyingfrogblog.blogspot.co.uk/2011/01/…
Jon Harrop

11
"Düşük seviyeli Unix stili çatallamayı nasıl yapardınız". Aynı şekilde GC'd gibi OCaml dilleri ~ 20 yıl veya daha uzun süredir yapıyor.
Jon Harrop

9
"Python küresel yorumlayıcı kilidini çoğunlukla çöp toplama nedeniyle sağladı". Strawman argümanı. Java ve .NET'in her ikisinin de GC'si vardır, ancak ikisinin de global kilitleri yoktur.
Jon Harrop

149

Buradaki tartışmaya eklemek için.

Çöp toplama ile ilgili bilinen sorunlar vardır ve bunları anlamak, C ++ 'da neden yok olduğunu anlamaya yardımcı olur.

1. Performans?

İlk şikayet genellikle performansla ilgilidir, ancak çoğu insan ne hakkında konuştuklarını gerçekten bilmez. Tarafından gösterildiği gibi Martin Beckett, kendi başına performansı, ama performans tahmin edilebilir olmayabilir sorun.

Şu anda yaygın olarak konuşlandırılan 2 GC ailesi var:

  • İşaretle ve Tara tipi
  • Referans Sayma türü

Daha Mark And Sweephızlıdır (genel performans üzerinde daha az etki), ancak "dünyayı dondur" sendromundan muzdariptir: yani GC devreye girdiğinde, GC temizliğini yapana kadar her şey durdurulur. Birkaç milisaniyede cevap veren bir sunucu oluşturmak isterseniz ... bazı işlemler beklentilerinize uygun olmayacaktır :)

Sorunu Reference Countingfarklıdır: referans sayma, özellikle Çoklu İş parçacığı ortamlarında ek yük ekler, çünkü atom sayımına ihtiyacınız vardır. Ayrıca referans döngüleri problemi vardır, bu nedenle bu döngüleri tespit etmek ve ortadan kaldırmak için akıllı bir algoritmaya ihtiyacınız vardır (genellikle daha az sıklıkta olsa da "dünyayı dondur" ile de uygulayın). Genel olarak, bugün itibariyle, bu tür (normalde daha duyarlı veya daha doğrusu, daha az sıklıkta donma olsa da) daha yavaştır Mark And Sweep.

Eyfel uygulayıcıları tarafından , "Dünyayı Dondur" yönü olmadan Reference Countingbenzer bir küresel performansa sahip olacak bir Çöp Toplayıcıyı uygulamaya çalışan bir makale gördüm Mark And Sweep. GC için ayrı bir diş gerektiriyordu (tipik). Algoritma biraz korkutucuydu (sonunda) ancak kağıt kavramları birer birer tanıtmak ve algoritmanın "basit" versiyondan tam teşekküllü olana evrimini göstermek için iyi bir iş çıkardı. PDF dosyama ellerimi geri koyabilseydim tavsiye edilir ...

2. Kaynak Alımı Başlatılıyor (RAII)

C++Kaynakların sahipliğini düzgün bir şekilde serbest bırakıldığından emin olmak için bir nesnenin içindeki mülkiyeti sarmanız yaygın bir deyimdir . Çöp koleksiyonumuz olmadığından çoğunlukla bellek için kullanılır, ancak yine de diğer birçok durum için yararlıdır:

  • kilitler (çoklu iş parçacığı, dosya tanıtıcısı, ...)
  • bağlantıları (bir veritabanına, başka bir sunucuya, ...)

Fikir, nesnenin ömrünü düzgün bir şekilde kontrol etmektir:

  • ihtiyacınız olduğu sürece hayatta olmalı
  • işiniz bittiğinde öldürülmeli

GC sorunu, eğer öncekine yardım ederse ve sonuçta daha sonra ... bu "nihai" nin yeterli olmayabileceğini garanti ederse. Bir kilidi bırakırsanız, kilidin şimdi serbest bırakılmasını istersiniz, böylece başka çağrıları engellemez!

GC'li dillerde iki çözüm vardır:

  • yığın tahsisi yeterli olduğunda GC kullanmayın: normalde performans sorunları içindir, ancak bizim durumumuzda kapsam ömrünü tanımladığı için gerçekten yardımcı olur
  • usingyapı ... ancak C ++ RAII'de açıkken (zayıf) RAII örtüktür, böylece kullanıcı farkında olmadan hata YAPAMAZ ( usinganahtar sözcüğü atlayarak )

3. Akıllı İşaretçiler

Akıllı işaretçiler genellikle belleği işlemek için gümüş bir kurşun gibi görünür C++. Çoğu zaman duydum: sonuçta GC'ye ihtiyacımız yok, çünkü akıllı işaretçilerimiz var.

Daha yanlış olamazdı.

Akıllı işaretçiler yardımcı olur: auto_ptrve unique_ptrgerçekten son derece kullanışlı olan RAII kavramlarını kullanın. O kadar basittir ki, bunları kendiniz kolayca yazabilirsiniz.

Sahipliği paylaşmak gerektiğinde daha da zorlaşırsa: birden fazla iş parçacığı arasında paylaşabilirsiniz ve sayımın ele alınmasında birkaç küçük sorun vardır. Bu nedenle, doğal olarak doğru gider shared_ptr.

Harika, sonuçta Boost bu, ama gümüş bir kurşun değil. Aslında, asıl mesele, shared_ptrtarafından uygulanan bir GC'yi taklit etmesidir, Reference Countingancak döngü algılamayı tek başına uygulamanız gerekir ...

Tabii ki bu var weak_ptr şey var, ama maalesef shared_ptrbu döngüler nedeniyle kullanılmasına rağmen bellek sızıntıları gördüm ... ve Çok Dişli bir ortamda olduğunuzda, tespit etmek son derece zordur!

4. çözüm nedir?

Gümüş mermi yok, ama her zamanki gibi kesinlikle mümkün. GC'nin yokluğunda, sahiplik konusunda net olmak gerekir:

  • mümkünse belirli bir zamanda tek bir sahibin olmasını tercih edin
  • değilse, sınıf diyagramınızın sahiplikle ilgili herhangi bir döngüsüne sahip olmadığından emin olun ve weak_ptr

Gerçekten de, bir GC'ye sahip olmak harika olurdu ... ancak önemsiz bir sorun değil. Ve bu arada, sadece kolları sıvamamız gerekiyor.


2
Keşke iki cevabı kabul edebilseydim! Bu harika. Performans açısından, ayrı bir iş parçacığında çalışan GC aslında oldukça yaygındır (Java ve .Net'te kullanılır). Bu gömülü sistemlerde kabul edilemeyebilir.
Jason Baker

14
Sadece iki tip? Koleksiyoncular nasıl kopyalanır? Nesil koleksiyoncular? Çeşitli eşzamanlı toplayıcılar (Baker'ın sert gerçek zamanlı koşu bandı dahil)? Çeşitli hibrit kollektörler? Dostum, bu alanın endüstrisindeki sırf cehalet beni bazen şaşırtıyor.
SADECE benim doğru GÖRÜŞÜM

12
Sadece 2 tip olduğunu söyledim mi? Yaygın olarak kullanılan 2 tane olduğunu söyledim. Bildiğim kadarıyla Python, Java ve C # şimdi Mark ve Sweep algoritmalarını kullanıyor (Java bir referans sayma algoritmasına sahipti). Daha da net olmak gerekirse, bana göre C #, küçük döngüler için Generational GC, büyük döngüler için Mark And Sweep ve bellek parçalanması ile mücadele etmek için Copying kullanıyor; ancak algoritmanın kalbinin Mark And Sweep olduğunu iddia ediyorum. Başka bir teknoloji kullanan herhangi bir ana dili biliyor musunuz? Öğrenmekten her zaman mutlu olurum.
Matthieu M.Haziran

3
Siz sadece üç dil kullanan bir ana dili söylediniz.
SADECE benim doğru GÖRÜŞÜM

3
Temel fark, Generational ve Incremental GC'nin dünyayı durdurmasını gerektirmemesi ve GC işaretleyicilerine (faktör toplama ihtiyacının temel bir tahmini ile birlikte yeni düğümlerin sayısı ile belirlenebilir). Tahminlerinizi iyileştirmenize olanak tanıyan düğümün oluşturulma / modifikasyonunun nerede yapıldığı hakkındaki verileri ekleyerek GC'yi daha da ileriye götürebilirsiniz ve bununla birlikte Escape Analizini ücretsiz olarak alabilirsiniz.
Keldon Alleyne

56

Ne tür? gömülü çamaşır makinesi kontrolörleri, cep telefonları, iş istasyonları veya süper bilgisayarlar için optimize edilmeli mi?
GUI yanıt verebilirliğine veya sunucu yüklemesine öncelik vermeli mi?
çok fazla bellek veya çok fazla CPU kullanmalı mı?

C / c ++ çok farklı durumlarda kullanılır. Ben akıllı işaretçiler artırmak gibi bir şey çoğu kullanıcı için yeterli olacağını düşünüyorum

Düzenle - Otomatik çöp toplayıcıları çok fazla bir performans sorunu değildir (her zaman daha fazla sunucu satın alabilirsiniz) bu öngörülebilir performans sorunudur.
GC'nin ne zaman devreye gireceğini bilmemek narkoleptik havayolu pilotu kullanmak gibidir, çoğu zaman harikadırlar - ama gerçekten duyarlılığa ihtiyacınız olduğunda!


6
Kesinlikle fikrinizi görüyorum, ama sormaya mecbur hissediyorum: Java neredeyse birçok uygulamada kullanılmıyor mu?
Jason Baker

35
Hayır. Java, C ++ ile aynı ölçüde performans garantisine sahip olmaması nedeniyle yüksek performanslı uygulamalar için uygun değildir. Böylece bir cep telefonunda bulacaksınız, ancak bir hücre anahtarında veya süper bilgisayarda bulamazsınız.
Zathrus

11
Her zaman daha fazla sunucu satın alabilirsiniz, ancak her zaman müşterinin cebinde cep telefonu için daha fazla CPU satın alamazsınız!
Crashworks

8
Java, CPU verimliliğinde çok fazla performans yakalama yaptı. Gerçekten zor olan sorun bellek kullanımıdır, Java C ++ 'dan doğal olarak daha az bellek verimlidir. Ve bu verimsizlik, toplanan çöp olduğu gerçeğinden kaynaklanmaktadır. Çöp toplama hem hızlı hem de bellek açısından verimli olamaz, GC algoritmalarının ne kadar hızlı çalıştığına bakarsanız ortaya çıkan bir gerçek.
Nate CK

2
@Zathrus java, gecikme olmasa da (gerçek zamanlı olarak boo) ve kesinlikle bellek ayak izinde olmasa da, optimizasyon jitinin b / c üretimini kazanabilir.
gtrak

34

C ++ 'ın çöp toplamada bulunmamasının en büyük nedenlerinden biri, yıkıcılarla güzel oynamak için çöp toplama işleminin gerçekten çok zor olmasıdır. Bildiğim kadarıyla, kimse henüz tamamen nasıl çözüleceğini bilmiyor. Başa çıkmak için bir sürü sorun var:

  • Nesnelerin deterministik yaşamları (referans sayımı size bunu verir, ancak GC bunu yapmaz. Her ne kadar büyük bir anlaşma olmayabilir).
  • nesne toplanırken bir yıkıcı fırlatırsa ne olur? Çoğu dil bu istisnayı görmezden gelir, çünkü onu taşıyabilecek bir catch bloğu yoktur, ancak bu muhtemelen C ++ için kabul edilebilir bir çözüm değildir.
  • Nasıl etkinleştirilir / devre dışı bırakılır? Doğal olarak bu bir derleme zamanı kararıdır, ancak GC'ye yazılan kod NOT NOT için yazılan kod çok farklı ve muhtemelen uyumsuz olacaktır. Bunu nasıl uzlaştırıyorsunuz?

Bunlar karşılaşılan sorunlardan sadece birkaçı.


17
GC ve yıkıcılar, Bjarne'den hoş bir yan bakışla çözülmüş bir sorundur. Yıkıcılar GC sırasında çalışmaz, çünkü GC'nin amacı bu değildir. C ++ 'da GC , sonsuz diğer kaynakları değil , sonsuz bellek kavramını yaratmak için vardır.
MSalters

2
Yıkıcılar çalışmazsa, bu dilin anlamını tamamen değiştirir. En azından yeni bir anahtar kelime "gcnew" ya da bir şey gerekir ki böylece açıkça bu nesnenin GC'ed izin (ve bu nedenle bellek dışında kaynakları sarmak için kullanmamalısınız).
Greg Rogers

7
Bu sahte bir argüman. C ++ açık bellek yönetimine sahip olduğundan, her nesnenin ne zaman serbest bırakılması gerektiğini bulmanız gerekir. GC ile daha da kötü değildir; bunun yerine, sorun, belirli nesneler serbest bırakıldığında çözülmeye, yani silindikten sonra özel dikkat edilmesi gereken nesnelere indirgenir. Java ve C # 'da deneyim programlama, nesnelerin büyük çoğunluğunun özel bir husus gerektirmediğini ve GC'ye güvenle bırakılabileceğini ortaya koymaktadır. Anlaşıldığı gibi, C ++ 'daki yıkıcıların ana işlevlerinden biri, GC'nin sizin için otomatik olarak işlediği alt nesneleri serbest bırakmaktır.
Nate CK

2
@ NateC-K: GC'de GC olmayan (belki de en büyük şey) olarak geliştirilen bir şey, sağlam bir GC sisteminin referans var olduğu sürece her referansın aynı nesneyi göstermeye devam edeceğini garanti etme yeteneğidir. DisposeBir nesneyi çağırmak nesneyi kararsız hale getirebilir, ancak nesneyi canlıyken işaret eden referanslar öldükten sonra da devam eder. Buna karşılık, GC olmayan sistemlerde, referanslar varken nesneler silinebilir ve nadiren bu referanslardan biri kullanılırsa mahvedilebilecek herhangi bir sınırlama vardır.
supercat

22

Bu eski bir soru olsa da , hiç kimsenin ele almadığını görmediğim bir sorun var: çöp toplama işlemini belirlemek neredeyse imkansız.

Özellikle, C ++ standardı, uygulamanın bu davranışı nasıl elde ettiğinden ziyade, dili harici olarak gözlemlenebilir davranış açısından belirtmek için oldukça dikkatlidir. Bununla birlikte, çöp toplama durumunda , hemen hemen dışarıdan gözlemlenebilir davranış yoktur.

genel düşünce çöp toplama bir bellek ayırma başarılı olacağınızı garanti makul bir girişimde gerektiğidir. Ne yazık ki, çalışan bir çöp toplayıcı olsa bile, herhangi bir bellek tahsisinin başarılı olacağını garanti etmek imkansızdır. Bu, her durumda, ancak özellikle C ++ durumunda bir dereceye kadar doğrudur, çünkü toplama döngüsü sırasında nesneleri bellekte hareket ettiren bir kopyalayıcı toplayıcı (veya benzer bir şey) kullanmak mümkün değildir.

Nesneleri taşıyamazsanız, ayırma işlemlerinizi yapmak için tek bir bitişik bellek alanı oluşturamazsınız - ve bu da yığınınızın (veya serbest mağazanızın veya aramayı tercih ettiğiniz her şeyin) yapabileceği ve muhtemelen , zamanla parçalanır. Bu da, talep edilen miktardan daha fazla bellek boşken bile bir ayırmanın başarılı olmasını engelleyebilir.

O ile gelip mümkün olabilir iken bazı art arda tahsisinin aynı desen tekrar eğer ki (özünde) diyor teminat ve ilk defa başarılı, o sağlanan, daha sonraki tekrarlamalar başarılı devam edeceğini ayrılan bellek yinelemeler arasında erişilemez hale geldi. Bu çok zayıf bir garanti, aslında işe yaramaz, ancak onu güçlendirmek için makul bir umut göremiyorum.

Yine de, C ++ için önerilenlerden daha güçlü. Bir önceki önerisi [uyarı: PDF] (düştü) değil hiç bir garanti bir şey yapmadı. 28 sayfalık teklifte, harici olarak gözlemlenebilir davranış biçiminde elde ettiğiniz şey, tek (normatif olmayan) bir nottu:

[Not: Çöp toplanan programlar için, yüksek kalitede barındırılan bir uygulama geri aldığı ulaşılamayan bellek miktarını en üst düzeye çıkarmaya çalışmalıdır. —End not]

En azından benim için bu , yatırım getirisi hakkında ciddi bir soru ortaya çıkarıyor. Mevcut kodu kıracağız (hiç kimse tam olarak ne kadar, ama kesinlikle biraz), uygulamalara yeni gereksinimler ve kod üzerindeki yeni kısıtlamalar koyacağız ve karşılığında aldığımız şey muhtemelen hiçbir şey değil mi?

En iyi ihtimalle bile, Java ile sınamaya dayalı olarak , muhtemelen şimdi yaptıkları hızda çalışmak için yaklaşık altı kat daha fazla bellek gerektirecek programlardır . Daha da kötüsü, çöp toplama en başından beri Java'nın bir parçasıydı - C ++, çöp toplayıcısına neredeyse kesinlikle daha kötü bir maliyet / fayda oranına sahip olacağı konusunda yeterince daha fazla kısıtlama getiriyor (teklifin garantili olanın ötesine geçsek ve olacağını varsayalım) bazı faydalar).

Durumu matematiksel olarak özetleyeceğim: bu karmaşık bir durum. Herhangi bir matematikçinin bildiği gibi, karmaşık bir sayının iki kısmı vardır: gerçek ve hayali. Bana öyle geliyor ki, gerçek maliyetler gerçek, ama (en azından çoğunlukla) hayali faydalar.


Biri düzgün çalışması için tüm nesnelerin silinmesi gerektiğini ve yalnızca silinen nesnelerin toplama için uygun olacağını belirtse bile, referans izleme çöp toplama için derleyici desteği hala yararlı olabilir , çünkü böyle bir dil Silinmiş bir işaretçinin (başvuru) kullanılmasının Tanımsız Davranışa neden olmak yerine tuzağa düşmesi garanti edilir.
supercat

2
Java'da bile, GC yararlı AFAIK için herhangi bir şey yapması için belirtilmemiştir. Sizi çağırabilir free( freeC diline benzer demek istiyorum ). Ancak Java asla kesinleştirici veya bunun gibi bir şeyi aramayı garanti etmez. Aslında, C ++, taahhüt veritabanı yazımları, kızarma dosya tanıtıcıları vb. Java'nın "GC" olduğunu iddia ediyor, ancak Java geliştiricileri her zaman titizlikle çağırmalı close()ve close()çok erken veya çok geç aramamaya dikkat ederek kaynak yönetiminin çok farkında olmalılar . C ++ bizi bundan kurtarır. ... (devam)
Aaron McDaid

2
.. bir an önce yaptığım yorum Java'yı eleştirmeyi amaçlamıyor. Sadece "çöp toplama" teriminin çok garip bir terim olduğunu gözlemliyorum - bu, insanların düşündüğünden çok daha az anlamına geliyor ve bu nedenle ne anlama geldiğini net olarak tartışmak zor.
Aaron McDaid

@AaronMcDaid GC'nin bellek dışı kaynaklara hiç yardımcı olmadığı doğrudur. Neyse ki, bu tür kaynaklar belleğe kıyasla oldukça nadiren tahsis edilir. Dahası, bunların% 90'ından fazlası onları tahsis eden yöntemde serbest bırakılabilir, bu yüzden try (Whatever w=...) {...}çözer (ve unuttuğunuzda bir uyarı alırsınız). Kalanlar RAII ile de sorunludur. Arayan close()hafızası her Java hattında neredeyse tahsis alır iken o kadar da kötü değil yani, belki bir kez bin hatları onlarca başına "her zaman" anlamına gelir.
maaartinus

15

Otomatik çöp toplama istiyorsanız, C ++ için iyi ticari ve kamu malı çöp toplayıcıları vardır. Çöp toplamanın uygun olduğu uygulamalar için, C ++, diğer çöp toplanan dillerle karşılaştırılabilir performansa sahip mükemmel bir çöp toplanmış dildir. C ++ ' da otomatik çöp toplama hakkında bir tartışma için C ++ Programlama Dili (4. Baskı) bölümüne bakınız . Ayrıca bakınız, Hans-J. Boehm'in C ve C ++ çöp toplama sitesi ( arşiv ).

Ayrıca, C ++, bellek yönetiminin bir çöp toplayıcı olmadan güvenli ve örtük olmasını sağlayan programlama tekniklerini destekler . Çöp toplamanın son bir seçim olduğunu ve kaynak yönetimi için mükemmel bir yol olmadığını düşünüyorum. Bu, asla yararlı olmadığı anlamına gelmez, sadece birçok durumda daha iyi yaklaşımlar olduğu anlamına gelir.

Kaynak: http://www.stroustrup.com/bs_faq.html#garbage-collection

Neden dahili olarak gelmediğine gelince, doğru hatırlıyorsam, GC bir şey olmadan icat edildi ve dilin birkaç nedenden dolayı GC'ye sahip olabileceğine inanmıyorum (IE Geriye C ile uyumluluk)

Bu yardımcı olur umarım.


"Çöp toplanan diğer dillerle karşılaştırılabilir bir performansla". Alıntı?
Jon Harrop

1
Bağlantım koptu. Bu cevabı 5 yıl önce yazdım.
Rayne

1
Tamam, bu iddiaların bağımsız bir şekilde doğrulanmasını umuyordum, yani Stroustrup veya Boehm tarafından değil. :-)
Jon Harrop

12

Stroustrup, 2013 Going Native konferansında bu konuda bazı iyi yorumlar yaptı.

Bu videoda yaklaşık 25m50'lere atlayın . (Aslında tüm videoyu izlemenizi öneririm, ancak bu çöp toplama ile ilgili öğelere atlar.)

Nesnelerle ve değerlerle doğrudan başa çıkmayı (açık bir şekilde) kullanmaktan kaçınmayı kolay (ve güvenli, öngörülebilir ve okunması kolay ve öğretmesi kolay) gerçekten harika bir diliniz olduğunda yığın, o zaman çöp toplama bile istemiyorum .

Modern C ++ ve C ++ 11'de sahip olduğumuz şeyler ile, çöp toplama sınırlı koşullar dışında artık arzu edilmemektedir. Aslında, büyük bir C ++ derleyicisinden birine iyi bir çöp toplayıcı yerleştirilmiş olsa bile, çok sık kullanılmayacağını düşünüyorum. GC'den kaçınmak daha zor değil, daha kolay olacaktır .

Bu örneği gösteriyor:

void f(int n, int x) {
    Gadget *p = new Gadget{n};
    if(x<100) throw SomeException{};
    if(x<200) return;
    delete p;
}

Bu C ++ 'da güvensizdir. Ama Java'da da güvensiz! C ++ 'da, işlev erken dönerse delete, hiçbir zaman çağrılmaz. Ancak, Java gibi tam bir çöp koleksiyonunuz varsa, nesnenin "gelecekte bir noktada" imha edileceği yönünde bir öneri alırsınız ( Güncelleme: bunun daha da kötüsü. Değilsonlandırıcıyı hiç aramaya söz veriyorum - belki de asla çağrılmaz). Gadget'ın açık bir dosya tanıtıcısı veya bir veritabanıyla bağlantısı veya daha sonra bir veritabanına yazmak için arabelleğe alınan veriler varsa bu yeterli değildir. Bu kaynakları mümkün olan en kısa sürede serbest bırakmak için Gadget'ın biter bitmez yok edilmesini istiyoruz. Veritabanı sunucunuzun artık gerekmeyen binlerce veritabanı bağlantısıyla uğraşmasını istemezsiniz - programınızın çalışmasının bittiğini bilmez.

Peki çözüm nedir? Birkaç yaklaşım var. Nesnelerinizin büyük çoğunluğu için kullanacağınız bariz yaklaşım:

void f(int n, int x) {
    Gadget p = {n};  // Just leave it on the stack (where it belongs!)
    if(x<100) throw SomeException{};
    if(x<200) return;
}

Bunu yazmak daha az karakter gerektirir. Araya girmesi yok new. Gadgetİki kez yazmanızı gerektirmez . Nesne, işlevin sonunda yok edilir. İstediğiniz buysa, bu çok sezgiseldir. veya ile Gadgetaynı davranır . Tahmin edilebilir, okunması kolay, öğretmesi kolay. Her şey bir 'değer'. Bazen büyük bir değerdir, ancak değerlerin öğretilmesi daha kolaydır, çünkü işaretçilerle (veya referanslarla) elde ettiğiniz bu 'uzaktan hareket' şeyiniz yoktur.intdouble

Yaptığınız nesnelerin çoğu, yalnızca onları oluşturan ve belki de alt işlevlere girdi olarak iletilen işlevde kullanım içindir. Programcı, nesneleri döndürürken veya başka şekilde yazılımın geniş olarak ayrılmış bölümlerinde paylaşırken 'bellek yönetimi' hakkında düşünmek zorunda olmamalıdır.

Kapsam ve ömür önemlidir. Çoğu zaman, ömrün kapsamla aynı olması daha kolaydır. Anlamak daha kolay ve öğretmek daha kolay. Farklı bir kullanım ömrü istediğinizde, bunu yaptığınız kodu shared_ptrörneğin kullanarak okumak açık olmalıdır . (Alternatif olarak, değeri (büyük), nesneleri geri hareket-semantik artırılması veya unique_ptr.

Bu bir verimlilik sorunu gibi görünebilir. Bir Gadget'ı iade etmek istersem ne olur foo()? C ++ 11'in hareket semantiği, büyük nesneleri döndürmeyi kolaylaştırır. Sadece yazın Gadget foo() { ... }ve sadece çalışacak ve çabucak çalışacaktır. &&Kendinizle uğraşmanıza gerek yok , sadece şeyleri değere göre döndürün ve dil genellikle gerekli optimizasyonları yapabilir. (C ++ 03'ten önce bile, derleyiciler gereksiz kopyalamayı önlemek için oldukça iyi bir iş çıkardı.)

Stroustrup'un videoda başka bir yerde söylediği gibi (yorumlama): "Sadece bir bilgisayar bilimcisi bir nesneyi kopyalamak ve sonra orijinali yok etmekte ısrar eder. (İzleyiciler güler) Neden nesneyi doğrudan yeni konuma taşımamak? (bilgisayar bilimcileri değil).

Bir nesnenin yalnızca bir kopyasının gerekli olduğunu garanti edebiliyorsanız, nesnenin ömrünü anlamak çok daha kolaydır. İstediğiniz yaşam boyu politikasını seçebilirsiniz ve isterseniz çöp toplama oradadır. Ancak diğer yaklaşımların faydalarını anladığınızda, çöp toplamanın tercihler listenizin altında olduğunu göreceksiniz.

Bu sizin için işe yaramazsa, kullanabilir unique_ptrveya başarısız olabilirsiniz shared_ptr. İyi yazılmış C ++ 11, bellek yönetimi söz konusu olduğunda diğer dillerden daha kısa, okunması kolay ve öğretimi daha kolaydır.


1
GC, yalnızca kaynak edinmeyen nesneler için kullanılmalıdır (diğer kuruluşlardan kendi adlarına "bir sonraki duyuruya kadar" bir şeyler yapmalarını istemek). Eğer Gadgetkendi adına bir şey yapmak için başka bir şey sormuyor (Java için) anlamsız eğer orijinal kod Java tamamen güvende olacağını deleteaçıklamaya çıkarıldı.
supercat

@supercat, sıkıcı yıkıcılara sahip nesneler ilginç. ('Sıkıcı' tanımlamamıştım, ama temelde hafızanın serbest bırakılması dışında, asla çağrılması gerekmeyen yıkıcılar). Bir derleyicinin 'sıkıcı' shared_ptr<T>olduğunda özel olarak işlemesi mümkün olabilir T. Bu tür için bir ref sayacını gerçekten yönetmemeye ve bunun yerine GC kullanmaya karar verebilir. Bu, GC'nin geliştiricinin fark etmesine gerek kalmadan kullanılmasına izin verecektir. A shared_ptrbasitçe bir GC işaretçisi olarak görülebilir T. Ancak bununla ilgili sınırlamalar vardır ve birçok programı yavaşlatır.
Aaron McDaid

İyi bir tür sistemi, GC ve RAII tarafından yönetilen yığın nesneleri için farklı türlere sahip olmalıdır, çünkü bazı kullanım kalıpları biri ile çok iyi, diğeriyle çok kötü çalışır. .NET veya Java, deyim string1=string2;(kelimenin tam anlamıyla bir kayıt yükü başka bir şey değildir ve mağaza kayıt) ve sağlamak için herhangi bir kilitleme gerektirmez dize uzunluğunun ne olursa olsun çok çabuk çalıştırır yukarıda beyan ederken yürütülürse string2DİR yazıldığında, string1Tanımlanmamış Davranış olmadan eski değeri veya yeni değeri tutar).
supercat

C ++ 'da, a'nın atanması shared_ptr<String>çok sayıda sahne arkası senkronizasyonu gerektirir ve Stringbir değişkenin eşzamanlı olarak okunması ve yazılması durumunda a'nın atanması tuhaf davranabilir. Birinin Stringaynı anda yazmak ve okumak isteyeceği durumlar çok yaygın değildir, ancak örneğin bazı kodlar devam eden durum raporlarını diğer iş parçacıkları için kullanılabilir hale getirmek isterse ortaya çıkabilir. .NET ve Java'da bu tür şeyler sadece "çalışır".
supercat

1
@curiousguy hiçbir şey değişmedi, doğru önlemleri almadığınız sürece, Java hala yapıcı biter bitirmez sonlandırıcının çağrılmasına izin verir. İşte gerçek hayattan bir örnek: “ Java 8'de ulaşılabilir nesnelere çağrılan finalize () ”. Sonuç, neredeyse herkesin dilin tarihsel tasarım hatası olmayı kabul ettiği bu özelliği asla kullanmamaktır. Bu tavsiyeye uyduğumuzda, dil sevdiğimiz determinizmi sağlar.
Holger

11

Çünkü modern C ++ çöp toplama gerektirmez.

Bjarne Stroustrup'un bu konudaki SSS cevabı şöyle diyor :

Çöpü sevmiyorum. Çöpü sevmem. Benim idealim, çöp üretmeden çöp toplayıcı ihtiyacını ortadan kaldırmak. Bu artık mümkün.


Bugünlerde yazılan kodun (C ++ 17 ve resmi Temel Yönergeleri takip eden ) durumu aşağıdaki gibidir:

  • Bellek sahipliği ile ilgili kodların çoğu kütüphanelerde (özellikle kapsayıcı sağlayanlar).
  • Bellek sahipliğini içeren kodun çoğu kullanımı RAII modelini takip eder , bu nedenle tahsis üzerine bir şey tahsis edilen kapsamdan çıkıldığında meydana gelen inşaat ve tahsisat üzerine tahsis yapılır.
  • Sen açıkça tahsis veya doğrudan deallocate bellek yok .
  • Ham işaretçiler belleğe sahip değildir (yönergeleri izlediyseniz), bu yüzden onları geçirerek sızdıramazsınız.
  • Bellekteki değer dizilerinin başlangıç ​​adreslerini nasıl geçireceğinizi merak ediyorsanız - bunu bir açıklık ile yapacaksınız ; ham işaretçiye gerek yok.
  • Gerçekten bir sahip "işaretçi" gerekiyorsa, C ++ ' standart kütüphane akıllı işaretçiler kullanın - onlar sızıntı olamaz ve son derece verimli ( ABI buna engel olabilir , ancak). Alternatif olarak, "sahiplik işaretçileri" ile sahipliğini kapsam sınırları içinden geçirebilirsiniz . Bunlar nadirdir ve açıkça kullanılmalıdır; ancak kabul edildiğinde - sızıntılara karşı güzel bir statik kontrole izin verir.

"Ah evet? Ama ne olacak ...

... eski günlerde C ++ yazmak için kullandığımız kodu yazarsam? "

Gerçekten de, tüm yönergeleri göz ardı edebilir ve sızdıran uygulama kodu yazabilirsiniz - ve her zamanki gibi derlenir ve çalışır (ve sızıntı yapar).

Ancak bu, geliştiricinin erdemli olması ve çok fazla öz denetim gerçekleştirmesi beklenen bir "bunu yapma" durumu değildir; uygun olmayan kod yazmak daha basit değildir, yazmak daha hızlı değildir ve daha iyi performans göstermez. Ayrıca, uygun kodun sağladığı ve beklediği ile artan bir "empedans uyumsuzluğu" ile karşılaşacağınız için, yazma işlemi daha da zorlaşacaktır.

... eğer ben reintrepret_cast? Yoksa karmaşık işaretçi aritmetiği mi? Yoksa bu tür hack'leri mi? "

Gerçekten de, zihninizi koyarsanız, yönergelerle iyi oynamasına rağmen işleri karıştıran kod yazabilirsiniz. Fakat:

  1. Bunu nadiren yaparsınız (koddaki yerler açısından, mutlaka yürütme süresinin kesirliği açısından değil)
  2. Bunu sadece kasıtlı olarak yaparsınız, yanlışlıkla değil.
  3. Bunu yapmak, kurallara uygun bir kod tabanında göze çarpacaktır.
  4. Yine de GC'yi başka bir dilde atlayacağınız kod türüdür.

... kütüphane geliştirme? "

Bir C ++ kütüphane geliştiricisiyseniz, ham işaretçiler içeren güvenli olmayan bir kod yazıyorsunuz ve dikkatlice ve sorumlu bir şekilde kodlamanız gerekiyor - ancak bunlar uzmanlar tarafından yazılan bağımsız kod parçalarıdır (ve daha da önemlisi, uzmanlar tarafından incelenir).


Yani, tıpkı Bjarne'nin dediği gibi: Genel olarak çöp toplamak için hiçbir motivasyon yoktur, hepiniz gibi, çöp üretmediğinizden emin olun. GC, C ++ ile sorun değil.

Bu, özel tahsis ve tahsis tahsisi stratejileri uygulamak istediğinizde, GC'nin belirli uygulamalar için ilginç bir sorun olmadığı anlamına gelmez. Dil düzeyinde GC değil, özel tahsis ve tahsisat ayırmayı istersiniz.


Dizeleri taşlıyorsanız (GC'ye ihtiyacınız var) .. Parçalı bir yapı oluşturduğunuzu, sonra farklı uzunluklarda işleyip yeniden oluşturduğunuzu, kullanılmayanları sildiğinizi, başkalarını birleştirdiğinizi vb. çünkü üstesinden gelmek için üst düzey dillere geçmek zorunda kaldım. (Elbette kendi GC'nizi de oluşturabilirsiniz).
www-0av-Com

2
@ user1863152: Bu, özel bir ayırıcının yararlı olacağı bir durumdur. Hala bir dil-integral GC gerektirmez ...
einpoklum

einpoklum'a: doğru. Kurslar için sadece at. Benim gereksinimim, Ulaşım Yolcu Bilgilerinin dinamik olarak değişen galonlarını işlemekti. Büyüleyici bir konu .. Gerçekten yazılım felsefesi geliyor.
www-0av-Com

Java ve .NET dünyasının keşfettiği GC'nin sonunda büyük bir sorunu var - ölçeklenmiyor. Önemsiz herhangi bir yazılımla bu günlerde yaptığımız gibi milyarlarca canlı nesneniz olduğunda, GC'den bir şeyler gizlemek için kod yazmaya başlamanız gerekir. Java ve .NET'te GC olması bir yüktür.
Zach Saw

10

C ++ 'ın arkasındaki fikir, kullanmadığınız özellikler için herhangi bir performans etkisi ödememenizdi. Yani çöp toplama eklemek, bazı programların C'nin yaptığı gibi doğrudan donanımda, bazılarının da bir çeşit çalışma zamanı sanal makinesinde çalışması anlamına geliyordu.

Hiçbir şey, bazı üçüncü taraf çöp toplama mekanizmasına bağlı bazı akıllı işaretçiler kullanmanızı engellemez. Microsoft'un COM ile böyle bir şey yaptığını hatırlıyorum ve iyi gitmedi.


2
GC'nin bir VM gerektirdiğini düşünmüyorum. Derleyici, genel bir durumu güncellemek için tüm işaretçi işlemlerine kod eklerken, gerektiğinde nesneleri silen arka planda ayrı bir iş parçacığı çalışır.
user83255

3
Katılıyorum. Sanal bir makineye ihtiyacınız yok, ama ikincisi hafızanızı sizin için arka planda böyle yönetmeye başladığınızda, gerçek "elektrik kablolarını" bıraktığınız ve bir çeşit VM durumuna sahip olduğunuzu hissediyorum.
Uri


4

Orijinal C dilinin arkasındaki temel ilkelerden biri, belleğin bir bayt dizisinden oluşması ve kodun sadece bu baytların tam olarak kullanıldıkları anda ne anlama geldiğine dikkat etmeleri gerektiğidir. Modern C, derleyicilerin ek kısıtlamalar getirmesine izin verir, ancak C, - ve C ++, bir işaretçiyi bayt dizisine ayrıştırma, aynı değerleri içeren herhangi bir bayt dizisini bir işaretçiye birleştirme ve daha sonra bu işaretçiyi kullanma önceki nesneye erişme.

Bu yetenek bazı uygulamalarda yararlı, hatta vazgeçilmez olsa da, bu yeteneği içeren bir dil, her türlü yararlı ve güvenilir çöp toplama işlemini destekleme becerisinde çok sınırlı olacaktır. Bir derleyici, bir işaretçi oluşturan bitlerle yapılan her şeyi bilmiyorsa, işaretçiyi yeniden oluşturmak için yeterli bilginin evrenin herhangi bir yerinde var olup olmadığını bilmenin bir yolu olmayacaktır. Bu bilginin, bilgisayarın, onlar hakkında bilgi sahibi olsa bile erişemeyeceği şekilde depolanması mümkün olacağından (örneğin, işaretçiyi oluşturan baytlar birisinin yazabileceği kadar uzun bir süre ekranda gösterilmiş olabilir) bir bilgisayarın üzerine yerleştirin), bir bilgisayarın gelecekte bir işaretçinin kullanılıp kullanılamayacağını bilmek tam anlamıyla imkansız olabilir.

Çöp toplanmış birçok çerçevenin ilginç bir tuhaflığı, içerdiği bit desenleri tarafından tanımlanmayan, ancak nesne başvurusunda tutulan bitler ile başka bir yerde tutulan diğer bilgiler arasındaki ilişkinin tanımlanmasıdır. C ve C ++ 'da, bir işaretçide saklanan bit paterni bir nesneyi tanımlarsa, o bit paterni, nesne açıkça yok edilene kadar o nesneyi tanımlar. Tipik bir GC sisteminde, bir nesne bir anda bir bit paterni 0x1234ABCD ile temsil edilebilir, ancak bir sonraki GC döngüsü 0x1234ABCD'ye yapılan tüm referansları 0x4321BABE referanslarıyla değiştirebilir, bunun üzerine nesne ikinci kalıp ile temsil edilir. Bir nesne referansı ile ilişkili bit desenini görüntüleyip daha sonra klavyeden geri okuyabilseniz bile,


Bu gerçekten iyi bir nokta, son zamanlarda benim işaretçilerimden bazı bitleri çaldım çünkü aksi takdirde aptalca önbellek özledikleri olurdu.
Passer By

@PasserBy: 64 bit işaretçiler kullanan kaç uygulamanın nesne referansları olarak ölçeklendirilmiş 32 bit işaretçiler kullanmasından ya da hemen hemen her şeyi 4GiB adres alanında tutmaktan ve verileri yükseklerden depolamak / almak için özel nesneleri kullanmaktan daha fazla fayda sağlayacağını merak ediyorum hızlı depolama alanı? Makineler, 64 bit işaretçilerin RAM tüketiminin önemli olmayabileceği yeterli RAM'e sahiptir, ancak 32 bit işaretçilerden iki kat daha fazla önbellek çakabilirler.
supercat

3

Tüm teknik konuşma kavramı aşırı karmaşıklaştırıyor.

GC'yi tüm bellek için otomatik olarak C ++ 'a koyarsanız, bir web tarayıcısı gibi bir şey düşünün. Web tarayıcısı tam bir web belgesi yüklemeli ve web komut dosyaları çalıştırmalıdır. Web komut dosyası değişkenlerini belge ağacında saklayabilirsiniz. Çok sayıda sekmesi açık olan bir tarayıcıdaki BÜYÜK bir belgede, GC'nin tam bir koleksiyon yapması gerektiğinde tüm belge öğelerini de taraması gerektiği anlamına gelir.

Çoğu bilgisayarda bu, SAYFA HATALARI oluşacağı anlamına gelir. Yani asıl neden, soruyu cevaplamak için SAYFA HATALARI oluşacaktır. Bunu bilgisayarınızın çok fazla disk erişimi yapmaya başladığı zaman bileceksiniz. Bunun nedeni, GC'nin geçersiz işaretçileri kanıtlamak için çok fazla belleğe dokunması gerektiğidir. Çok fazla bellek kullanan iyi niyetli bir uygulamanız olduğunda, SAYFA HATALARI nedeniyle her koleksiyondaki tüm nesneleri taramak zorunda kalırsınız. Sayfa hatası, sanal belleğin diskten RAM'a yeniden okunması gerektiğinde ortaya çıkar.

Bu nedenle doğru çözüm, bir uygulamayı GC'ye ihtiyaç duyan parçalara ve gerekmeyen parçalara bölmektir. Yukarıdaki web tarayıcısı örneğinde, belge ağacı malloc ile ayrılmış, ancak javascript GC ile çalıştırılmışsa, GC her başladığında hafızanın sadece küçük bir bölümünü ve belleğin tüm PAGED OUT öğelerini tararsa belge ağacının tekrar sayfalanmasına gerek yoktur.

Bu sorunu daha iyi anlamak için sanal belleğe ve bu bilgisayarın bilgisayarlara nasıl uygulandığına bakın. Aslında bu kadar RAM olmadığı zaman 2GB'ın program tarafından kullanılabilir olması gerçeğiyle ilgilidir. 32BIt sistemi için 2GB RAM bulunan modern bilgisayarlarda, sadece bir program çalışıyorsa böyle bir sorun olmaz.

Ek bir örnek olarak, tüm nesneleri izlemesi gereken tam bir koleksiyonu düşünün. Öncelikle köklerden erişilebilen tüm nesneleri taramalısınız. İkinci adım 1'de görünen tüm nesneleri tarayın. Sonra bekleyen yıkıcıları tarayın. Ardından tüm sayfalara tekrar gidin ve görünmez tüm nesneleri kapatın. Bu, birçok sayfanın birden çok kez değiştirilip geri alınabileceği anlamına gelir.

Bu yüzden kısaltmak için verdiğim yanıt, tüm belleğe dokunmanın bir sonucu olarak ortaya çıkan SAYFA HATALARI sayısının bir programdaki tüm nesneler için tam GC'ye olanaksız olması ve böylece programcının GC'yi komut dosyaları gibi şeyler için bir yardım olarak görmesi gerektiğidir. ve veritabanı çalışması, ancak manuel bellek yönetimi ile normal şeyler yapmak.

Ve elbette çok önemli bir diğer neden de küresel değişkenler. Toplayıcının genel değişken değişkeninin GC'de olduğunu bilmesi için belirli anahtar kelimeler gerektirir ve bu nedenle mevcut C ++ kodu çalışmaz.


3

KISA CEVAP: Çöp toplamanın verimli bir şekilde (küçük zaman ve alan yükü ile) ve her zaman doğru şekilde (mümkün olan her durumda) nasıl yapılacağını bilmiyoruz.

UZUN CEVAP: C gibi, C ++ da bir sistem dilidir; Bu, sistem kodu, örneğin işletim sistemi yazarken kullanıldığı anlamına gelir. Başka bir deyişle, C ++, tıpkı C gibi, mümkün olan en iyi performansla tasarlanmıştır gibi, ana hedef olarak . Dil standardı, performans hedefini engelleyebilecek hiçbir özellik eklemeyecektir.

Bu şu soruyu duraklatır: Çöp toplama neden performansı engelliyor? Bunun ana nedeni, uygulama söz konusu olduğunda, [bilgisayar bilimcileri] tüm durumlar için minimum yük ile çöp toplama işleminin nasıl yapılacağını bilmiyoruz. Bu nedenle, C ++ derleyicisi ve çalışma zamanı sisteminin çöp toplama işlemini her zaman verimli bir şekilde gerçekleştirmesi imkansızdır. Öte yandan, bir C ++ programcısı tasarımını / uygulamasını bilmeli ve çöp toplama işleminin en iyi nasıl yapılacağına karar verecek en iyi kişidir.

Son olarak, kontrol (donanım, ayrıntılar, vb.) Ve performans (zaman, alan, güç vb.) Ana kısıtlar değilse, C ++ yazma aracı değildir. Diğer dil, daha iyi hizmet verebilir ve gerekli ek yük ile daha [gizli] çalışma zamanı yönetimi sunabilir.


3

C ++ 'ı Java ile karşılaştırdığımızda, C ++' ın örtülü Çöp Toplama göz önünde bulundurularak tasarlanmadığını görüyoruz.

C-Style'da keyfi işaretçiler gibi şeylere sahip olmak sadece GC uygulamaları için kötü değildir, aynı zamanda büyük miktarda C ++ - eski kod için geriye dönük uyumluluğu da yok eder.

Buna ek olarak, C ++, karmaşık bir çalışma zamanı ortamına sahip olmak yerine bağımsız yürütülebilir olarak çalıştırılması amaçlanan bir dildir.

Sonuç olarak: Evet, C ++ 'a Çöp Toplama eklemek mümkün olabilir, ancak süreklilik için bunu yapmamak daha iyidir.


1
Hafızayı boşaltmak ve yıkıcıları çalıştırmak tamamen ayrı konulardır. (Java'nın bir PITA olan yıkıcıları yoktur.) GC belleği boşaltır, dtors çalıştırmaz.
curiousguy

0

Temelde iki nedenden dolayı:

  1. Çünkü buna ihtiyacı yok (IMHO)
  2. Çünkü C ++ 'nın temel taşı olan RAII ile hemen hemen uyumsuz

C ++ zaten manuel bellek yönetimi, yığın tahsisi, RAII, kaplar, otomatik işaretçiler, akıllı işaretçiler sunuyor ... Bu yeterli olmalı. Çöp toplayıcılar, kimin hangi nesnelere veya kaynakların serbest bırakılması gerektiğine dair 5 dakika geçirmek istemeyen tembel programcılar içindir. C ++ ile böyle şeyler yapmıyoruz.


Çöp toplama olmadan uygulanması zor olan çok sayıda (daha yeni) algoritma vardır. Zaman ilerledi. Yenilik ayrıca (çöp toplama) üst düzey dillere iyi uyan yeni bilgilerden de kaynaklanmaktadır. Bunlardan herhangi birini GC ücretsiz C ++ 'a backport yapmaya çalışın, yoldaki tümsekleri fark edeceksiniz. (Örnek vermem gerektiğini biliyorum, ama şu anda biraz acelem var. Üzgünüm. Şu anda düşünebildiğim bir tanesi, referans sayımının çalışmadığı kalıcı veri yapıları etrafında dönüyor.).
BitTickler

0

Çöp toplama empoze etmek gerçekten düşük seviyeden yüksek seviyeye paradigma değişimidir.

Dizelerin çöp toplama özelliğindeki bir dilde işlenme biçimine bakarsanız, SADECE yüksek düzey dize işleme işlevlerine izin verdiğini ve dizelere ikili erişime izin vermediklerini göreceksiniz. Basitçe söylemek gerekirse, tüm dize işlevleri, yalnızca bir bayt çiziyor olsanız bile, dizenin nerede olduğunu görmek için işaretçileri kontrol eder. Bu nedenle, çöp toplama özelliğine sahip bir dizedeki her baytı işleyen bir döngü yapıyorsanız, dizenin ne zaman taşındığını bilemediğinden, her yineleme için temel konumu artı kaymayı hesaplaması gerekir. Sonra yığınlar, yığınlar, iplikler vb.

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.