Belleğin ne zaman çıkarılacağını statik olarak tahmin etmek mümkün mü --- yalnızca kaynak koddan?


27

Bellek (ve kaynak kilitleri) bir programın yürütülmesi sırasında belirleyici noktalarda işletim sistemine geri gönderilir. Bir programın kendi başına kontrol akışı kesin olarak, belirli bir kaynağın nerede dağıtılabileceğini bilmek için yeterlidir. Tıpkı bir programcının fclose(file)programın ne zaman yapıldığını nasıl yazacağını bildiği gibi.

GC'ler bunu, kontrol akışı yürütüldüğünde çalışma zamanında doğrudan çözerek çözerler. Ancak kontrol akışıyla ilgili gerçek gerçeğin kaynağı kaynaktır. Bu yüzden teorik olarak, free()kaynağı (veya AST) analiz ederek çağrıları derlemeden önce nereye ekleyeceğinizi belirlemek mümkün olmalıdır .

Referans sayımı bunu uygulamanın açık bir yoludur, ancak işaretçilerin hala başvuruda bulunduğu (hala kapsamda) ancak artık ihtiyaç duyulmayan durumlarla karşılaşmak kolaydır. Bu sadece işaretçilerin manuel olarak dağıtılması sorumluluğunu, işaretçilerin kapsamını / referanslarını manuel olarak yönetme sorumluluğuna dönüştürür.

Bir programın kaynağını okuyabilen bir program yazmak mümkün gibi görünüyor:

  1. programın kontrol akışının tüm izinlerini --- programın canlı çalışmasını izlemeye benzer doğrulukta tahmin edin
  2. tahsis edilmiş kaynaklara yapılan tüm referansları takip edin
  3. Her referans için, referansın asla geri alınamayacağının garantilendiği en eski noktayı bulmak için tüm kontrol akışını çaprazlayın.
  4. Bu noktada, kaynak kodunun bu satırına bir gider kodu ekleyiniz.

Dışarıda bunu yapan bir şey var mı? Rust veya C ++ akıllı işaretçiler / RAII aynı şey olduğunu sanmıyorum.


57
durma problemine bak. Bu, neden "Bir programın X yapıp yapmadığını bir derleyici çözemez mi?" Sorusunun dedesidir. Her zaman "Genel durumda değil" ile yanıtlanır.
cırcır ucube

18
Bellek (ve kaynak kilitleri) bir programın yürütülmesi sırasında belirleyici noktalarda işletim sistemine geri gönderilir. No.
Euphoric

9
@ratchetfreak Teşekkürler, bu durma problemi gibi şeyleri bilemem, beni kimya yerine bilgisayar bilimlerinde almamı diliyor.
zelcon

15
@ zelcon5, şimdi kimya ve durma problemini biliyorsunuz ... :)
David Arno

7
@Euphoric Bir kaynak kullanıldığında sınırları RAII ile veya deneyin-ile-kaynaklar gibi çok açıktır böylece program yapısı sürece
mandal ucube

Yanıtlar:


23

Bu (tartışmalı) örneği ele alalım:

void* resource1;
void* resource2;

while(true){

    int input = getInputFromUser();

    switch(input){
        case 1: resource1 = malloc(500); break;
        case 2: resource2 = resource1; break;
        case 3: useResource(resource1); useResource(resource2); break;
    }
}

Ücretsiz ne zaman çağrılmalıdır? malloc ve atama resource1işleminden resource2önce kopyalanamaz çünkü kopyalanamaz , atamadan önce alamazız resource2çünkü kullanıcıdan iki kez müdahale etmeden iki kez almış olabiliriz.

Emin olmanın tek yolu, durum 1 ve 2'de eşit olmadıklarını görmek için resource1 ve resource2'yi test etmek ve eğer olmadıklarında eski değeri serbest bırakmaktır. Bu aslında sadece 2 olası referans bulunduğunu bildiğiniz referans sayımıdır.


Aslında bu tek yol değil; diğer yol ise sadece bir kopyanın var olmasına izin vermektir . Bu, elbette, kendi sorunları ile birlikte geliyor.
Jack Aidley

27

RAII otomatik olarak aynı şey değildir, ancak aynı etkiye sahiptir. “Buna artık ne zaman erişilemez?” Sorusuna kolay bir cevap veriyor. Belirli bir kaynak kullanıldığında alanı kapsayacak şekilde kapsamı kullanarak .

Benzer bir problemi göz önünde bulundurmak isteyebilirsiniz "programımın çalışma zamanında bir tür hatayla karşılaşmayacağını nasıl bilebilirim?". Bu çözüm değildir , böyle bir hata olamaz kanıtlamak için program sayesinde ama tür ek açıklaması ve çıkarım bir sistem kullanarak, tüm uygulama yolları tahmin. Rust, bu ispat özelliğini bellek ayırmaya genişletme girişimidir.

Durma problemini çözmek zorunda kalmadan programın davranışına dair kanıtlar yazmak mümkündür, ancak yalnızca programı kısıtlamak için bir tür açıklama kullanıyorsanız. Ayrıca bakınız güvenlik kanıtları (sel4 vb.)


Yorumlar genişletilmiş tartışmalar için değildir; bu konuşma sohbete taşındı .
maple_shaft

13

Evet, bu vahşi doğada var. ML Kit , mevcut bellek yönetimi seçeneklerinden biri olarak tanımlanan stratejiye (az ya da çok) sahip, üretim kalitesinde bir derleyicidir. Ayrıca, geleneksel bir GC kullanımına veya referans sayımı ile hibritleşmesine izin verir (hangi stratejinin programınız için en iyi sonuçları üreteceğini görmek için bir yığın profili kullanabilirsiniz).

Bölgeye dayalı bellek yönetimine ilişkin geriye dönük bir bakış , ML Kit'in yazarları tarafından başarılarına ve başarısızlıklarına giren bir makaledir. Nihai sonuç, bir yığın profil oluşturucu yardımı ile yazarken stratejinin pratik olduğudur.

(Bu, pratik mühendislik sorularının yanıtı için genellikle Halting Problemine neden bakmamanız gerektiğinin iyi bir örneğidir: çoğu gerçekçi program için genel durumu çözmek istemiyoruz veya çözmemiz gerekiyor .)


5
Bence bu, Halting Probleminin doğru uygulanmasının mükemmel bir örneği. Durma problemi bize sorunun genel olarak çözülemez olduğunu söyler, bu yüzden problemin çözülebilir olduğu sınırlı senaryolar ararsınız.
Taemyr

Standart ML ve Haskell gibi saf veya neredeyse saf fonksiyonel, yan etkisi olmayan dillerden bahsettiğimizde sorunun çok daha çözülebilir olduğunu unutmayın
cat

10

programın kontrol akışının tüm izinlerini tahmin et

Sorunun yattığı yer burasıdır. Önemsiz olmayan herhangi bir program için permütasyon miktarı o kadar büyüktür (pratikte sınırsızdır), gerekli zaman ve hafıza bunu tamamen pratik yapmaz.


iyi bir nokta. Ben kuantum işlemciler hiç herhangi varsa, sadece umut vardır tahmin
ZELCON

4
@ zelcon5 Haha, hayır. Kuantum hesaplama bunu daha da kötüleştiriyor , daha da kötüleştiriyor . Programa ek ("gizli") değişkenler ve çok daha fazla belirsizlik ekler. Gördüğüm en pratik QC kodu "hızlı hesaplama için kuantum, onay için klasik" e dayanıyor. Kuantum bilgisayarlarda kendi yüzeyimi zar zor çizdim ama bana göre kuantum bilgisayarların, klasik bilgisayarların desteklenmesi ve sonuçlarının kontrol edilmesi için çok faydalı olamayacağı da görülüyor.
Luaan

8

Durma problemi, bunun her durumda mümkün olmadığını kanıtlar. Bununla birlikte, birçok durumda hala mümkündür ve aslında, neredeyse bütün derleyiciler tarafından muhtemelen değişkenlerin çoğu için yapılır. Bir derleyicinin, uzun vadeli yığın depolama yerine yalnızca yığına bir değişken veya hatta bir kayıt ayırmanın güvenli olduğunu söyleyebileceği durum budur.

Saf işlevleriniz veya gerçekten iyi bir mülkiyet semantiğiniz varsa, bu statik analizi daha da genişletebilirsiniz, ancak bunu yapmak için daha masraflı hale gelse de, kodunuzun daha fazla dalını almasını sağlar.


Derleyici hafızayı boşa çıkarabileceğini düşünüyor ; ama öyle olmayabilir. Bir işaretçi veya bir yerel değişkene bir referans döndürmek için genel acemi hatasını düşünün. Önemsiz vakalar derleyici tarafından yakalanır, doğru; daha az önemsiz olanlar değildir.
Peter - Monica

Bu hata ile yapılır programcılar dilde programcılar elle bellek ayırma @Peter yönetmesi gerekir. Ne zaman derleyici bellek ayırmayı yönetir, hatalar bu tür gerçekleşmez.
Karl Bielefeldt

Pekala, C derleyicilerini içermesi gereken "hemen hemen tüm derleyiciler" ifadesini içeren çok genel bir açıklama yaptınız.
Peter - Monica

2
C derleyicileri, kayıtlara hangi geçici değişkenlerin tahsis edilebileceğini belirlemek için kullanır.
Karl Bielefeldt

4

Tek bir programcı veya ekip bütün programı yazarsa, belleğin (ve diğer kaynakların) serbest bırakılması gereken tasarım noktalarının tanımlanması mantıklıdır. Bu nedenle, evet, tasarımın statik analizi daha sınırlı bağlamlarda yeterli olabilir.

Bununla birlikte, üçüncü taraf DLL'lerini, API'leri, çerçeveleri (ve ayrıca konu başlıklarını da attığınızda) hesaba kattığınızda, programcıların hangi hafızanın ve hangi hafızanın sahibi olduğu hakkında doğru bir şekilde sebep olmaları çok zor olabilir (hayır, her durumda imkansız). son kullanımı olduğunda. Her zamanki dil şüphelimiz, sığ ve derin nesnelerin ve dizilerin hafıza sahipliğini aktarmayı yeterince belgelendirmiyor. Eğer bir programcı bunun nedeni olamazsa (statik ya da dinamik olarak!) O zaman bir derleyici büyük olasılıkla ya da yapamaz. Yine, bu, bellek sahipliği transferlerinin yöntem çağrıları veya arayüzler vb. Tarafından yakalanmaması nedeniyledir, bu nedenle, kodun ne zaman veya nerede bellekte yer alacağını statik olarak tahmin etmek mümkün değildir.

Bu kadar ciddi bir problem olduğu için birçok modern dil, en son canlı referanstan bir süre sonra otomatik olarak belleği geri alan çöp koleksiyonunu seçer. GC, (özellikle gerçek zamanlı uygulamalar için) önemli bir performans maliyetine sahiptir, ancak evrensel bir tedavi de değildir. Ayrıca, GC kullanarak hafıza sızıntılarına hala sahip olabilirsiniz (örneğin, yalnızca büyüyen bir koleksiyon). Yine de, bu çoğu programlama alıştırması için iyi bir çözümdür.

Bazı alternatifler var (bazıları ortaya çıkıyor).

Rust dili RAII'yi aşırı derecede zorluyor. Mülkiyetin, bir arayan ve arayan arasında veya daha uzun ömürlü nesneler arasında ödünç alınmasına karşı ödünç alınan nesnelere, örneğin sınıf ve arabirim yöntemlerinde daha ayrıntılı olarak tanımlayan dilsel yapılar sağlar. Bellek yönetimine karşı yüksek seviyede bir derleme zamanı güvenliği sağlar. Bununla birlikte, almak önemsiz bir dil değildir ve problemsiz de değildir (örneğin, tasarımın tamamen sabit olduğunu sanmıyorum, bazı şeyler hala denenmekte ve dolayısıyla değişmekte).

Swift ve Objective-C, çoğunlukla otomatik referans sayma olan başka bir rotaya gider. Referans sayımı çevrimlerle ilgili sorunlara girer ve özellikle kapaklarla ilgili önemli programlayıcı zorlukları vardır.


3
Elbette, GC'nin maliyeti vardır, ancak performans avantajları da vardır. Örneğin, .NET'te, yığıntan ayırma işlemi neredeyse ücretsizdir, çünkü "yığın ayırma" desenini kullanır - sadece bir işaretçiyi artırır, ve bu kadar. .NET GC çevresinde yeniden yazılmış, manuel bellek ayırma kullandıklarından daha hızlı çalışan uygulamalar gördüm, kesin olarak kesin değil. Benzer şekilde, referans sayımı aslında oldukça pahalıdır (yalnızca bir GC'den farklı yerlerde) ve eğer kaçınmanız durumunda ödemek istemediğiniz bir şey. Gerçek zamanlı performans istiyorsanız, statik ayırma genellikle hala tek yoldur.
Luaan

2

Bir program bilinmeyen herhangi bir girişe bağlı değilse, evet, mümkün olmalıdır (bunun karmaşık bir görev olabileceği ve uzun sürebilir; Bu tür programlar derleme zamanında tamamen çözülebilir olacaktı; C ++ terimlerle (neredeyse) tamamen constexprs'den oluşabilirler . Basit örnekler, pi'nin ilk 100 basamağını hesaplamak veya bilinen bir sözlüğü sıralamak olabilir.


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.