Bellek sızıntıları ne kadar ileri gidebilir?


118

Birçok kez bellek sızıntılarıyla karşılaştım. Genellikle mallocyarın yokmuş gibi konuştuğumda veya FILE *kirli çamaşırlar gibi sallanıp durduğumda . Genelde tüm belleğin en azından program sona erdiğinde temizlendiğini varsayıyorum (okuyun: umutsuzca). Program sona erdiğinde veya çöktüğünde sızan belleğin toplanmayacağı durumlar var mı?

Cevap dilden dile büyük ölçüde değişiyorsa, o zaman C (++) 'a odaklanalım.

Lütfen 'yarın yokmuş gibi' ve 'kirli çamaşırlar gibi sallanan' ifadelerinin hiperbolik kullanımına dikkat edin. Güvensiz * malloc* yapmak sevdiklerinize zarar verebilir. Ayrıca kirli çamaşırlarda dikkatli olun.


3
Linux veya Windows gibi "modern" bir işletim sistemi ile çalışıyorsanız, işletim sistemi, program sona erdiğinde yayınlanmamış bellekleri kendisi çözecektir.
Oliver Charlesworth

60
Yarın yokmuş gibi alışveriş yapmak yerine, bir yarın varmış gibi davranmaya çalışın ve hafızanızın kaydını tutun!
William Pursell

8
@WilliamPursell ah, yani birinin callocyarın olmadığını sevmesi gerektiğini söylüyorsun . Mükemmel.
DilithiumMatrix

8
"Cevap dilden dile büyük ölçüde değişiyorsa, c (++) 'a odaklanalım." c ve c ++ aynı dil değildir!
Johnsyweb

11
@zhermes: C ve C ++ 'nın farklı diller olduğuna dair yorum sandığınızdan daha fazlasını gizler ... C ++' da kendinizi otomatik depolama süresi olan nesnelerden yararlanmayı tercih edersiniz, RAII deyimini takip edin ... bu nesnelerin hafızayı halletmesine izin verin sizin için yönetim.
LihO

Yanıtlar:


111

Hayır. İşletim sistemleri, çıktıklarında süreçler tarafından tutulan tüm kaynakları serbest bırakır.

Bu, işletim sisteminin koruduğu tüm kaynaklar için geçerlidir: bellek, açık dosyalar, ağ bağlantıları, pencere tutamaçları ...

Bununla birlikte, program bir işletim sistemi olmadan gömülü bir sistemde veya çok basit veya hatalı bir işletim sistemiyle çalışıyorsa, bellek yeniden başlatılıncaya kadar kullanılamayabilir. Ama bu durumda olsaydın, muhtemelen bu soruyu sormazdın.

İşletim sisteminin belirli kaynakları serbest bırakması uzun zaman alabilir. Örneğin, bir ağ sunucusunun bağlantıları kabul etmek için kullandığı TCP bağlantı noktasının, program tarafından düzgün bir şekilde kapatılmış olsa bile, serbest hale gelmesi dakikalar alabilir. Ağa bağlı bir program, veritabanı nesneleri gibi uzak kaynakları da tutabilir . Uzak sistem, ağ bağlantısı kesildiğinde bu kaynakları serbest bırakmalıdır, ancak yerel işletim sisteminden daha uzun sürebilir.


5
RTOS'larda ortak bir paradigma, tek işlem, çoklu iş parçacığı modeli ve 'görevler' arasında bellek koruması olmamasıdır. Genellikle bir yığın vardır. Kesinlikle VxWorks eskiden böyle çalışıyordu ve muhtemelen hala çalışıyor.
marko

29
Tüm kaynakların işletim sistemi tarafından serbest bırakılamayacağını unutmayın. Ağ bağlantıları, veritabanı işlemleri vb. Bunların açık bir şekilde kapatılmaması bazı istenmeyen sonuçlara neden olabilir. Ağ bağlantısını kapatmamak, sunucunun belirsiz bir süre boyunca hala aktif olduğunuzu düşünmesine neden olabilir ve aktif bağlantı sayısını sınırlayan sunucular için yanlışlıkla hizmet reddine neden olabilir. Veritabanı işlemlerini kapatmamak, kaydedilmemiş verileri kaybetmenize neden olabilir.
Lie Ryan

1
@Marko: vxWorks'ün son sürümü artık bellek korumasını destekleyen RTP'leri (gerçek zamanlı işlemler) destekliyor.
Xavier T.

20
"İşletim sistemleri, çıktıklarında süreçler tarafından tutulan tüm kaynakları serbest bırakır." Kesinlikle doğru değil. Örneğin, (en azından) Linux'ta, SysV semaforları ve diğer IPC nesneleri işlem çıkışında temizlenmez. Bu yüzden ipcrmmanuel temizleme için linux.die.net/man/8/ipcrm var .
sleske

7
Ayrıca, bir nesnenin sakladığı geçici bir dosyası varsa, bu daha sonra açıkça temizlenmeyecektir.
Mooing Duck

47

C Standardı, tarafından ayrılan belleğin mallocprogram sona erdiğinde serbest bırakıldığını belirtmez . Bu, işletim sistemi tarafından yapılır ve tüm işletim sistemleri (genellikle bunlar gömülü dünyada bulunur) program sona erdiğinde belleği serbest bırakmaz.


20
Bu aşağı yukarı çünkü C standardı C programlarından bahsediyor, C'nin çalıştığı işletim sistemlerinden değil ...
vonbrand

5
@vonbrand C Standardı, maintarafından ayrılan tüm belleğin geri döndüğünde mallocserbest bırakıldığını söyleyen bir paragrafa sahip olabilirdi . Örneğin, program sonlandırılmadan önce tüm açık dosyaların kapatıldığını söylüyor. My tahsis edilen bellek için mallocbelirtilmemiştir. Şimdi, tabii ki işletim sistemi ile ilgili cümlem, bu konuda hiçbir şey belirtmediği için, Standardın öngördüğü şeyleri değil, genellikle neyin yapıldığını açıklıyor.
ouah

Yorumumu düzeltmeme izin verin: Standart programın nasıl başlatılıp durdurulduğundan değil, C'den bahsediyor. İşletim sistemi olmadan çalışan bir C programı yazabilirsiniz . Bu durumda temizlik yapacak kimse yoktur. Standart çok gerekli olmadıkça kasten böylece gerek kalmadan değil kısıtlayan kullanımlara gibi bir şey belirtmez.
vonbrand

2
@ouah: " zaman ana döner ...". Bu bir varsayım. " Ana geri dönerse ..." diye düşünmeliyiz . std::atexitayrıca std::exit, aracılığıyla program sonlandırmayı da dikkate alır ve sonra ayrıca std::abortve (C ++ 'ya özgü) vardır std::terminate.
MSalters

@ouah: Bu dahil edilmiş atexitolsaydı , kullanılamazdı. :-)
R .. GitHub DUR YARDIMCI ICE

28

Tüm yanıtlar, sorunuzun modern işletim sistemleriyle ilgili birçok yönünü kapsadığı için, ancak tarihsel olarak, DOS dünyasında programladıysanız bahsetmeye değer bir tane var. Terminant ve Stay Yerleşik (TSR) programları genellikle kontrolü sisteme geri döndürür, ancak bir yazılım / donanım kesintisi ile yeniden canlandırılabilen bellekte kalır. Bu işletim sistemleri üzerinde çalışırken "bellek yetersiz! TSR'lerinizden bazılarını kaldırmayı deneyin" gibi mesajlar görmek normaldi .

Teknik olarak program sona erer , ancak yine de bellekte bulunduğu için, herhangi bir bellek sızıntısı, programı kaldırmadığınız sürece serbest bırakılmaz.

Dolayısıyla, bunu, hatalı olduğu için veya gömülü işletim sistemi bunu yapacak şekilde tasarlandığı için, işletim sistemlerinden başka bir durum olarak düşünebilirsiniz.

Bir örnek daha hatırlıyorum. Öncelikle IBM ana bilgisayarlarında çalışan bir işlem sunucusu olan Müşteri Bilgi Kontrol Sistemi (CICS) sözde konuşmaya dayanır. Yürütüldüğünde, kullanıcı tarafından girilen verileri işler, kullanıcı için başka bir veri kümesi oluşturur, kullanıcı terminal düğümüne aktarır ve sona erer. Dikkat anahtarı etkinleştirildiğinde, başka bir veri kümesini işlemek için yeniden canlanır. Teknik olarak yine davranış şeklinden dolayı, CICS işlem sunucusunu geri dönüştürmediğiniz sürece, işletim sistemi sonlandırılan CICS Programlarından belleği geri kazanmaz.


Bu gerçekten ilginç, tarihi not için teşekkürler! Bu paradigmanın, belleği boşaltmanın, gerekli değilse, hesaplama açısından çok maliyetli olmasından mı kaynaklandığını biliyor musunuz? Veya alternatif henüz hiç düşünülmemiş miydi?
DilithiumMatrix

1
@zhermes: DOS, TSR'ler için bellek ayırmalarını izlemediğinden, hesaplama açısından imkansızdı. Tanım gereği: amaç Yerleşik Kalmaktı . TSR'nizin hafızanın tamamını değil bir kısmını serbest bırakmasını istiyorsanız, neyi serbest bırakacağınıza karar vermek size kalmıştı.
MSalters

2
@zhermes: DOS (atası olan CP / M gibi) modern anlamda bir işletim sistemi dediğiniz şey değildi. Bu gerçekten sadece bir seferde bir program çalıştırmanıza izin veren bir komut işlemcisi ile birlikte standart bir şekilde çağrılabilecek bir G / Ç yardımcı programları koleksiyonuydu. Süreç kavramı yoktu ve bellek ne sanal ne de korumalıydı. TSR'ler, sisteme 64K'ya kadar yer kapladıklarını söyleyebilen ve çağrılmaları için kendilerini kesintilere bağlayan yararlı bir hack'ti.
Blrfl

8

Diğerlerinin söylediği gibi, çoğu işletim sistemi, işlemin sona ermesi üzerine (ve muhtemelen ağ soketleri, dosya tanıtıcıları vb. Gibi diğer kaynaklar) ayrılmış belleği geri kazanacaktır.

Bunu söyledikten sonra, yeni / sil ile uğraşırken endişelenmeniz gereken tek şey bellek olmayabilir (ham malloc / ücretsiz yerine). Yeniye ayrılan bellek geri kazanılabilir, ancak nesnelerin yıkıcılarında yapılabilecek şeyler olmayacaktır. Belki de bir sınıfın yıkıcısı, yok edildiğinde bir dosyaya gözcü bir değer yazar. İşlem hemen sonlanırsa, dosya tanıtıcısı temizlenebilir ve bellek geri alınabilir, ancak bu gözcü değer yazılmaz.

Hikayenin ahlaki, her zaman kendinizden sonra temizleyin. İşlerin sallanmasına izin vermeyin. Sizden sonra temizleyen işletim sistemine güvenmeyin. Kendinizden sonra temizleyin.


Sizden sonra işletim sisteminin temizlenmesine güvenmeyin. Kendinizden sonra temizleyin. ' Bu genellikle karmaşık çok iş parçacıklı uygulamalarda "çok, çok zor" bir durumdur. Bir kaynağa yapılan tüm referansların kaybolduğu gerçek sızıntılar kötüdür. Referansları açıkça yayınlamak yerine işletim sisteminin temizlenmesine izin vermek her zaman kötü değildir ve çoğu zaman alınacak tek makul yol budur.
Martin James

1
C ++ ve yok ediciler olacaktır (bazı az daha parlak sürece programın sona ermesi üzerine denilen olsun kill -9fan gösterileri yukarı ...)
vonbrand

@vonbrand True, ancak dinamik nesnelerle ilgili sızıntılardan bahsediyorsak, bu yıkıcılar gerçekleşmeyecek. Kapsam dışına çıkan nesne ham bir göstericidir ve yıkıcısı işlemsizdir. (Elbette, bu sorunu hafifletmek için RAII nesnelerine bakın ...)
Andre Kostur

1
RAII ile ilgili sorun, aslında kurtulmanın gerçekten önemli olmadığı süreç çıkışında nesnelerin serbest bırakılmasında ısrar etmesidir. Dikkatli olmak istediğiniz DB bağlantıları, ancak genel bellek en iyi işletim sistemi tarafından temizlenir (çok daha iyi bir iş çıkarır). Sorun , sayfalandırılan bellek miktarı arttığında çıkması kesinlikle yaşlanan bir program olarak kendini gösterir . Çözmesi de önemsiz değil…
Donal Fellows

@vonbrand: O kadar basit değil. std::exitdtors arayacak, std::abortolmayacak, yakalanmamış istisnalar olabilir.
MSalters

7

Bunun dilden çok işletim sistemine bağlı olması muhtemeldir. Sonuçta herhangi bir dildeki herhangi bir program belleğini işletim sisteminden alacaktır.

Bir program çıktığında / çöktüğünde belleği geri dönüştürmeyen bir işletim sistemi duymadım. Öyleyse, programınızın ayırması gereken bellek üzerinde bir üst sınırı varsa, o zaman sadece ayırmak ve asla serbest bırakmak tamamen mantıklıdır.


Basit bir işletim sistemi durumunda çekirdeğin bellek görüntüsünü bozabilir misiniz? .. Çok görevli olmayan işletim sistemleri gibi.
ulidtko

@ulidtko, bu olacak işleri batırmaya. Programım arada bir 1GiB demeyi gerektiriyorsa ve bu süre boyunca bunu yakalıyorsa, bu kaynakları kullanmıyorken bile başkalarına kullanmayı reddediyor demektir. Bu bugün önemli olabilir veya olmayabilir. Ama çevre edecek kökten değiştirin. Garantili.
vonbrand

@vonbrand Modern işletim sistemleri şu anda aktif olmayan bitleri sayfalandırabildiğinden, 1GiB'nin nadir kullanımı normalde bir sorun değildir (bol miktarda fiziksel belleğiniz olduğu sürece). Sorun, aktif kullanımda barındıracağınız fiziksel belleğinizden daha fazla sanal belleğiniz olduğunda ortaya çıkar .
Donal Fellows

5

Program, başka bir programın adres alanına yüklenen dinamik bir bileşene ("eklenti") dönüştürülürse, düzenli bellek yönetimi olan bir işletim sisteminde bile sorun yaratacaktır. Daha az yetenekli sistemlere taşınan kod hakkında düşünmemize bile gerek yok.

Öte yandan, tüm belleği serbest olabilir bir programın temizleme performansını etkileyebilir.

Üzerinde çalıştığım bir program, belirli bir test senaryosunun programdan çıkması için 30 saniye veya daha uzun bir süre gerekiyordu, çünkü tüm dinamik belleğin grafiğinde yineleniyor ve onu parça parça serbest bırakıyordu.

Makul bir çözüm, orada yeteneğe sahip olmak ve bunu test senaryolarıyla örtmek, ancak uygulamayı üretim kodunda kapatmak, böylece uygulamanın hızlı bir şekilde kapanmasıdır.


5

Ünvanı hak eden tüm işletim sistemleri, fesih işleminden sonra yaptığınız karışıklığı temizleyecektir. Ama her zaman öngörülemeyen olaylar vardır, ya erişim bir şekilde reddedilirse ve bazı kötü programcılar bu olasılığı önceden görmediyse ve biraz sonra tekrar denemeseydi? Hafıza sızıntıları kritik önem taşıyorsa kendinizi temizlemek her zaman daha güvenlidir - aksi takdirde bu çaba maliyetliyse IMO çabasına gerçekten değmez.

Düzenleme: Döngülerde olduğu gibi birikecekleri yerdeyse bellek sızıntılarını temizlemeniz gerekir. Bahsettiğim bellek sızıntıları, program boyunca sürekli olarak biriken sızıntılardır, eğer başka türden bir sızıntı varsa, er ya da geç büyük olasılıkla ciddi bir sorun olacaktır.

Teknik terimlerle, eğer sızıntılarınız bellek 'karmaşıklığı' ise O (1) çoğu durumda sorun değil, O (logn) zaten tatsız (ve bazı durumlarda ölümcül) ve O (N) + dayanılmaz.


3

POSIX uyumlu sistemlerdeki paylaşılan bellek, shm_unlink çağrılana veya sistem yeniden başlatılana kadar devam eder.


2

İşlemler arası iletişiminiz varsa, bu, protokole bağlı olarak diğer işlemlerin asla tamamlanmamasına ve kaynakları tüketmesine neden olabilir.

Bir örnek vermek gerekirse, bir keresinde bir yazıcı işinin ortasında JVM'yi sonlandırdığımda Java'da bir PDF yazıcısına yazdırmayı deniyordum, PDF biriktirme işlemi aktif kaldı ve yapamadan önce görev yöneticisinde onu öldürmek zorunda kaldım yeniden yazdırmayı deneyin.

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.