Çöp toplama neden diğer kaynak türlerine değil, yalnızca belleğe yayılıyor?


12

İnsanların manuel bellek yönetiminden bıktıkları anlaşılıyor, bu yüzden çöp toplamayı icat ettiler ve hayat makul derecede iyiydi. Peki ya diğer kaynak türleri? Dosya tanımlayıcıları, soketler ve hatta veritabanı bağlantıları gibi kullanıcı tarafından oluşturulan veriler?

Bu naif bir soru gibi geliyor ama kimsenin sorduğu bir yer bulamıyorum. Dosya tanımlayıcıları ele alalım. Bir programın, yalnızca başladığında 4000 fds'ye izin verileceğini bildiğini varsayalım. Dosya tanımlayıcısını açacak bir işlem gerçekleştirdiğinde, ya

  1. Bitmek üzere olmadığından emin olmak için kontrol edin.
  2. Öyleyse, bir sürü bellek boşaltacak çöp toplayıcıyı tetikleyin.
  3. Hafızanın bir kısmı dosya tanımlayıcılarına tutulan referansları serbest bıraktıysa, hemen kapatın. Bu kaynağa bağlı bellek, ilk açıldığında daha iyi bir terim bulunmaması nedeniyle bir 'dosya tanımlayıcı kayıt defterine' kaydedildiğinden, belleğin bir kaynağa ait olduğunu bilir.
  4. Yeni bir dosya tanımlayıcı açın, yeni belleğe kopyalayın, bu bellek konumunu 'dosya tanımlayıcı kayıt defterine' kaydedin ve kullanıcıya geri gönderin.

Bu yüzden kaynak derhal serbest bırakılmayacaktı, ancak en azından hiç kullanılmadığı varsayılarak, kaynak tükenmek üzereyken, en azından gc'yi içeren her çalıştırıldığında serbest bırakılacaktı.

Bu, birçok kullanıcı tanımlı kaynak temizleme sorunu için yeterli gibi görünüyor. Burada bir kaynak için bir başvuru içeren bir iş parçacığı ile C ++ buna benzer temizlik yapan referansları bulmayı başardı ve sadece (tek bir başvuru (temizleme iş parçacığı) kalan zaman temizler, ama ben ' bunun bir kütüphane ya da mevcut herhangi bir dilin parçası olduğuna dair herhangi bir kanıt bulamazlar.

Yanıtlar:


4

GC , öngörülebilir ve ayrılmış bir kaynakla ilgilenir . Sanal Makine üzerinde tam denetime sahiptir ve hangi örneklerin ne zaman oluşturulacağı üzerinde tam denetime sahiptir. Buradaki anahtar kelimeler "ayrılmış" ve "toplam kontrol" dür. Kollar OS tarafından tahsis edilir ve işaretçiler ... yönetilen alanın dışında tahsis edilen kaynaklara iyi işaretçilerdir. Bu nedenle, tutamaçların ve işaretçilerin yönetilen kod içinde kullanılması kısıtlanmaz. Aynı işlemde çalışan yönetilen ve yönetilmeyen kodlar tarafından kullanılabilirler ve çoğu zaman kullanılabilirler.

Bir "Kaynak Toplayıcı" yönetilen bir alanda bir tanıtıcı / işaretçi kullanılıp kullanılmadığını doğrulayabilir, ancak tanım gereği bellek alanı dışında neler olup bittiğinden habersizdir (ve işleri daha da kötüleştirmek için bazı tutamaçlar kullanılabilir) süreç sınırları boyunca).

Pratik bir örnek .NET CLR'dir. Hem yönetilen hem de yönetilmeyen bellek alanlarıyla çalışan kod yazmak için aromalı C ++ kullanabilirsiniz; tanıtıcılar, işaretçiler ve referanslar yönetilen ve yönetilmeyen kodlar arasında aktarılabilir. Yönetilmeyen kod, CLR'nin yönetilen kaynaklara yapılan referansları izlemesini sağlamak için özel yapılar / türler kullanmalıdır. Ama yapabileceği en iyi şey bu. Kulplar ve işaretçiler için de aynı şeyi yapamaz ve bu nedenle Kaynak Toplayıcı belirli bir tutamacı veya işaretçiyi serbest bırakmanın uygun olup olmadığını bilemez.

edit: .NET CLR ile ilgili olarak, ben .NET platformu ile C ++ geliştirme ile deneyimli değilim. Belki CLR'nin yönetilen ve yönetilmeyen kodlar arasındaki tanıtıcılara / göstericilere yapılan referansları izlemeye devam etmesini sağlayan özel mekanizmalar vardır. Bu durumda, CLR bu kaynakların ömrünü halledebilir ve bunlara yapılan tüm referanslar silindiğinde bunları serbest bırakabilir (en azından bazı senaryolarda olabilir). Her iki durumda da, en iyi uygulamalar, tutamaçların (özellikle dosyalara işaret edenlerin) ve işaretçilerin gerekli olmadıkları anda serbest bırakılması gerektiğini belirtir. Bir Kaynak Toplayıcı buna uymayacaktır, bu bir kaynak olmamak için başka bir nedendir.

edit 2: Genel olarak CLR / JVM / VM'lerde, yalnızca yönetilen alan içinde kullanılıyorsa, belirli bir tanıtıcıyı serbest bırakmak için bazı kodlar yazmak nispeten önemsizdir. .NET'te şöyle bir şey olurdu:

// This class offends many best practices, but it would do the job.
public class AutoReleaseFileHandle {
    // keeps track of how many instances of this class is in memory
    private static int _toBeReleased = 0;

    // the threshold when a garbage collection should be forced
    private const int MAX_FILES = 100;

    public AutoReleaseFileHandle(FileStream fileStream) {
       // Force garbage collection if max files are reached.
       if (_toBeReleased >= MAX_FILES) {
          GC.Collect();
       }
       // increment counter
       Interlocked.Increment(ref _toBeReleased);
       FileStream = fileStream;
    }

    public FileStream { get; private set; }

    private void ReleaseFileStream(FileStream fs) {
       // decrement counter
       Interlocked.Decrement(ref _toBeReleased);
       FileStream.Close();
       FileStream.Dispose();
       FileStream = null;
    }

    // Close and Dispose the Stream when this class is collected by the GC.
    ~AutoReleaseFileHandle() {
       ReleaseFileStream(FileStream);
    }

    // because it's .NET this class should also implement IDisposable
    // to allow the user to dispose the resources imperatively if s/he wants 
    // to.
    private bool _disposed = false;
    public void Dispose() {
      if (_disposed) {
        return;
      }
      _disposed = true;
      // tells GC to not call the finalizer for this instance.
      GC.SupressFinalizer(this);

      ReleaseFileStream(FileStream);
    }
}

// use it
// for it to work, fs.Dispose() should not be called directly,
var fs = File.Open("path/to/file"); 
var autoRelease = new AutoReleaseFileHandle(fs);

3

Bu, çöp toplayıcılı dillerin sonlandırıcıları uygulama nedenlerinden biri olduğu görülmektedir. Sonlandırıcıların, bir programcının çöp toplama sırasında bir nesnenin kaynaklarını temizlemesine izin vermesi amaçlanmıştır. Sonlandırıcılarla ilgili en büyük sorun, çalıştırılmalarının garanti edilmemesidir.

Burada sonlandırıcıların kullanımı hakkında oldukça iyi bir yazı var:

Nesne sonlandırma ve temizleme

Aslında örnek olarak dosya tanımlayıcıyı kullanır. Bu tür kaynakları kendiniz temizlediğinizden emin olmalısınız, ancak düzgün bir şekilde serbest bırakılmayan kaynakları geri yükleyebilecek bir mekanizma vardır.


Bunun soruma cevap verip vermediğinden emin değilim. Teklifimin, sistemin bir kaynağın tükenmek üzere olduğunu bildiği kısmı eksik. Bu parçayı çekiçlemenin tek yolu, yeni dosya tanımlayıcıları ayırmadan önce gc'yi manuel olarak çalıştırmanızı sağlamaktır, ancak bu son derece verimsizdir ve gc'nin java'da çalışmasına neden olup olamayacağınızı bilmiyorum.
mindreader

Tamam, ancak dosya tanımlayıcıları genellikle işletim sisteminde kilitler, arabellek havuzları, yapı havuzları vb. Gibi sistem düzeyi kaynakları kullandığını ima eden bir Açık Dosyayı temsil eder. Açıkçası, bu yapıları daha sonraki bir çöp toplama için açık bırakmanın faydasını görmüyorum ve onları gereğinden fazla tahsis etmede birçok zarar görüyorum. Finalize () yöntemleri, bir programcının kaynakları temizlemeye yönelik çağrıları gözden kaçırması, ancak buna güvenilmemesi durumunda son bir hendek temizliğine izin vermeyi amaçlamaktadır.
Brian Hibbert

Anladığım kadarıyla, güvenilmemelerinin nedeni, bu kaynakların bir tonunu tahsis ederseniz, belki de her dosyayı açan bir dosya hiyerarşisine iniyorsanız, gc gerçekleşmeden önce çok fazla dosya açabilmenizdir. patlamaya neden olur. Aynı şey bellekte de olur, ancak çalışma zamanı belleğin tükenmediğinden emin olmak için kontrol eder. Patlamadan önce rasgele kaynakları geri kazanmak için neden bir sistemin uygulanamayacağını, bellekle neredeyse aynı şekilde bilmek istiyorum.
mindreader

Bir sistem bellek dışındaki GC kaynaklarına yazılabilir, ancak referans sayılarını izlemeniz veya bir kaynağın artık kullanılmadığı zamanı belirlemek için başka bir yönteminizin olması gerekir. Hala kullanımda olan kaynakları ayırmak ve yeniden tahsis etmek istemezsiniz. Tüm iş parçacığı malikâtı bir iş parçacığının yazma için açık bir dosyası varsa, işletim sistemi dosya tanıtıcısını "geri alır" ve başka bir iş parçacığı aynı tanıtıcıyı kullanarak yazmak için farklı bir dosya açar. Ve yine de, GC gibi bir iplik onları serbest bırakmak için etrafta dolaşana kadar onları açık bırakmak önemli kaynak israfı olduğunu öneriyorum.
Brian Hibbert

3

Bu tür kaynakları yönetmeye yardımcı olacak birçok programlama tekniği vardır.

  • C ++ programcıları genellikle Kaynak Edinme Başlatma veya kısaca RAII adlı bir desen kullanır . Bu desen, kaynakları tutan bir nesne kapsam dışına çıktığında, üzerinde tuttuğu kaynakları kapatmasını sağlar. Bu, nesnenin ömrü programdaki belirli bir kapsama karşılık geldiğinde (örn., Yığın üzerinde belirli bir yığın çerçevesinin mevcut olduğu zamanla eşleştiğinde) yararlıdır, bu nedenle yerel değişkenler (işaretçi) tarafından işaretlenen nesneler için yararlıdır değişkenler yığın üzerinde saklanır), ancak öbek üzerinde depolanan işaretçilerle gösterilen nesneler için çok yararlı değildir.

  • Java, C # ve diğer birçok dil, bir nesne artık kullanılmadığında ve çöp toplayıcı tarafından toplanmak üzere çağrıldığında çağrılacak bir yöntem belirtmek için bir yol sağlar. Örneğin, sonlandırıcılar dispose()ve diğerlerine bakın. Fikir, programcının böyle bir yöntemi uygulayabilmesidir, böylece nesne çöp toplayıcı tarafından serbest bırakılmadan önce kaynağı açıkça kapatır. Bununla birlikte, bu yaklaşımların başka yerlerde okuyabileceğiniz bazı sorunları vardır; örneğin, çöp toplayıcı nesneyi istediğiniz kadar geç toplamayabilir.

  • C # ve diğer diller using, kaynaklara artık ihtiyaç duyulmadığında kapatılmasını sağlayan bir anahtar kelime sağlar (bu nedenle dosya tanımlayıcısını veya başka bir kaynağı kapatmayı unutmayın). Bu genellikle nesnenin artık canlı olmadığını keşfetmek için çöp toplayıcısına güvenmekten daha iyidir. Bkz . Örneğin, https://stackoverflow.com/q/75401/781723 . Buradaki genel terim yönetilen bir kaynaktır . Bu kavram RAII ve finalizörler üzerine kuruludur ve bazı açılardan gelişir.


Hızlı kaynak dağıtımı ile daha az ilgileniyorum ve tam zamanında anlaşma ile daha fazla ilgileniyorum. RIAA harika, ancak birçok çöp toplama dili için süper geçerli değil. Java'nın belirli bir kaynağın ne zaman tükenmek üzere olduğunu bilme yeteneği yoktur. Braket tipi işlemleri kullanmak yararlıdır ve hatalarla ilgilenir, ancak bunlarla ilgilenmiyorum. Sadece kaynakları tahsis etmek istiyorum ve daha sonra uygun veya gerekli olduğunda kendilerini temizleyecekler ve onu sıkıştırmanın çok az yolu var. Sanırım kimse buna gerçekten bakmadı.
mindreader

2

Tüm bellek eşittir, eğer 1K istersem, 1K'nın adres alanının neresinden geldiğini umursamıyorum.

Bir dosya tanıtıcı istediğimde, açmak istediğim dosya için tanıtıcı istiyorum. Bir dosyada açık dosya tanıtıcısı olması, genellikle dosyaya diğer işlemler veya makine tarafından erişimi engeller.

Bu nedenle, dosya tanıtıcıları gerekli olmadıkları anda kapatılmalıdır, aksi takdirde dosyaya diğer erişimleri engeller, ancak yalnızca dosya bitmeye başladığında belleğin geri kazanılması gerekir.

Bir GC geçişi çalıştırmak maliyetlidir ve yalnızca “gerektiğinde” yapılır, başka bir işlemin işleminizin artık kullanamayabileceği, ancak yine de açık olduğu bir dosya tanıtıcısına ne zaman ihtiyaç duyacağını tahmin etmek mümkün değildir.


Cevabınız gerçek tuşa çarpıyor: bellek değiştirilebilir ve çoğu sistem özellikle hızlı bir şekilde geri kazanılması gerekmeyecek kadar yeterli. Buna karşılık, bir program bir dosyaya özel erişim elde ederse, evrende her yerde, başka kaç dosya olursa olsun, bu dosyayı kullanması gerekebilecek diğer programları engelleyecektir.
Supercat

0

Bunun diğer kaynaklar için çok fazla yaklaşılmamasının nedenini tam olarak görüyorum, çünkü diğer kaynakların çoğunun herkesin tekrar kullanması için mümkün olan en kısa sürede serbest bırakılması tercih ediliyor.

Elbette, örneğinizin artık mevcut GC tekniklerine sahip "zayıf" dosya tanımlayıcıları kullanılarak sağlanabileceğini unutmayın.


0

Belleğin daha uzun süre erişilip erişilemediğini (ve artık kullanılmaması garanti edilir) kontrol etmek oldukça kolaydır. Diğer birçok kaynak türü aşağı yukarı aynı tekniklerle ele alınabilir (yani, kaynak edinimi başlatma, RAII ve kullanıcı yok edildiğinde bellek yönetimine bağlanan serbest bırakma karşılığıdır). Bir çeşit "tam zamanında" serbest bırakmak genel olarak imkansızdır (durma problemini kontrol edin, bazı kaynakların son kez kullanıldığını bulmak zorundasınız). Evet, bazen otomatik olarak yapılabilir, ancak bellek olarak çok daha karışık bir durumdur. Bu yüzden çoğunlukla kullanıcı müdahalesine dayanır.

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.