Programımın kullanım ömrü boyunca bir parça hafıza kullanmam gerekiyorsa, programın sonlandırılmasından hemen önce boşaltmak gerekli midir?


67

Birçok kitap ve derste, bellek yönetimi pratiğinin vurguladığını duydum ve kullandıktan sonra belleği boş bırakmazsam bazı gizemli ve korkunç şeylerin olacağını hissettim.

Diğer sistemler için konuşamam (benim için benzer bir uygulamayı benimsemelerini kabul etmem makul olsa da), ancak en azından Windows'ta, Çekirdek tarafından kullanılan kaynakların çoğunu (tuhaf birkaç hariç) temizleme temel olarak garanti edilir. program sonlandırıldıktan sonra bir program. Çeşitli şeylerin yanı sıra, yığın belleği içerir.

Kullanıcının kullanımına açmak için neden kullandıktan sonra bir dosyayı neden kapatmak istediğinizi ya da bant genişliğinden tasarruf etmek için neden sunucuya bağlı bir soket bağlantısını kesmek istediğinizi anlıyorum. Programınız tarafından kullanılan TÜM belleğinizi mikro yönetmeniz gerekir.

Şimdi, bu soru size gereken ne kadar bellek dayanmaktadır sayesinde hafıza nasıl işlemesi gerektiğini beri geniş olduğunu kabul ve ihtiyacınız olduğunda, bu yüzden bu, bu sorunun kapsamını daraltmak olacaktır: Ben bir parça kullanmak gerekiyorsa programımın ömrü boyunca hafıza, programın sonlandırılmasından hemen önce boşaltmak gerçekten gerekli mi?

Düzenleme: Çift olarak önerilen soru, Unix işletim sistemleri ailesine özgüdür. En üstteki cevabı Linux'a özgü bir araç bile belirledi (örneğin Valgrind). Bu soru çoğu "normal" yerleşik olmayan işletim sistemini ve bu programın kullanım ömrü boyunca gerekli olan belleği boşaltmanın neden iyi bir uygulama olduğunu ya da neden olmadığını içerir.



19
Bu dili agnostik olarak etiketlediniz, ancak birçok dilde (örn. Java) belleği manuel olarak serbest bırakmak gerekmez; bir nesneye yapılan son referansın kapsam dışına çıkmasından bir süre sonra otomatik olarak gerçekleşir
Richard Tingle

3
ve tabii ki C ++ 'ı tek bir silme olmadan yazabilirsiniz ve hafıza için iyi olurdu
Nikko

5
@RichardTingle Başımın üstünde C ve belki de C ++ dışında başka bir dil düşünemiyorum, ancak dil-agnostik etiketi, içinde fazla çöp toplama aracı bulunmayan tüm dilleri kapsayacak şekilde yapıldı. Bu kuşkusuz bugünlerde nadir görülür. Şimdi böyle bir sistemi C ++ 'ta uygulayabileceğinizi savunabilirsiniz, ancak sonuçta hala bir bellek parçasını silmeme seçeneğiniz vardır.
Yüzbaşı,

4
Siz yazıyorsunuz: "Çekirdek program sonlandırıldıktan sonra tüm kaynakları temizlemede temel olarak garanti altına alınmaktadır [...]". Bu genellikle yanlıştır, çünkü her şey hafıza, bir tutamaç veya bir çekirdek nesnesi değildir. Ancak, sorunuzu derhal sınırladığınız bellek için de geçerlidir.
Eric Towers

Yanıtlar:


108

Programımın kullanım ömrü boyunca bir parça hafıza kullanmam gerekiyorsa, programın sonlandırılmasından hemen önce boşaltmak gerekli midir?

Zorunlu değildir, ancak faydaları olabilir (bazı sakıncaları olduğu gibi).

Program yürütme süresi boyunca bir kez bellek ayırırsa ve aksi takdirde işlem bitene kadar serbest bırakmazsa, belleği manuel olarak serbest bırakmak ve işletim sistemine güvenmemek mantıklı bir yaklaşım olabilir. Bildiğim her modern işletim sisteminde, bu güvenlidir, işlemin sonunda tüm ayrılmış bellek sisteme güvenilir bir şekilde geri gönderilir.
Bazı durumlarda, tahsis edilen hafızanın açıkça temizlenmemesi, temizleme işleminden çok daha hızlı olabilir.

Ancak, uygulama sonunda tüm belleği serbest bırakarak,

  • hata ayıklama / test sırasında mem sızıntı tespit araçları size "yanlış pozitif" göstermiyor
  • Hafızayı kullanan kodu, tahsisat ve serbest bırakmayla birlikte ayrı bir bileşene taşımak ve daha sonra farklı bir bağlamda kullanmak, hafızanın kullanım süresinin bileşenin kullanıcısı tarafından kontrol edilmesi gerektiği daha kolay olabilir.

Programların ömrü değişebilir. Belki de programınız bugün küçük bir komut satırı yardımcı programıdır, tipik kullanım ömrü 10 dakikadan azdır ve her 10 saniyede bir kb'lik bölümler halinde bellek ayırır - bu nedenle programın bitmesinden önce ayrılmış belleği boşaltmaya gerek kalmaz. Daha sonra program değiştirilir ve birkaç hafta kullanım ömrüne sahip bir sunucu işleminin bir parçası olarak genişletilmiş bir kullanım alanı elde eder; bu nedenle, aralarında kullanılmayan hafızayı boşaltmamak artık bir seçenek değildir, aksi halde programınız tüm mevcut sunucu hafızasını tüketmeye başlar. . Bu, tüm programı gözden geçirmeniz ve sonradan ayrılma kodu eklemeniz gerektiği anlamına gelir. Şanslıysanız, bu kolay bir iştir, eğer değilse, o kadar zor olabilir ki, yüksek bir yer özledim. Ve bu durumda olduğunuzda, "özgür" ü eklemiş olmayı dileyeceksiniz

Daha genel olarak, yazma ve tahsis etme ile ilgili tahsisat kodunu yazmak, her zaman ikili olarak birçok programcı arasında "iyi bir alışkanlık" olarak sayılır: bunu her zaman yaparak, hafızanın serbest bırakılması gereken durumlarda tahliye kodunu unutma olasılığını azaltırsınız .


12
İyi programlama alışkanlıkları geliştirmek için iyi bir nokta.
Lawrence

4
Genellikle bu tavsiyeye şiddetle katılıyorum. İyi alışkanlıkların hafızada tutulması çok önemlidir. Ancak, istisnalar olduğunu düşünüyorum. Örneğin, tahsis ettiğiniz şey "düzgün" bir şekilde dolaşmak ve serbest bırakmak için birkaç saniye harcayacak çılgın bir grafikse, o zaman programı sonlandırarak ve işletim sistemine izin vererek kullanıcı deneyimini geliştirirsiniz.
GrandOpener

1
Her modern "normal" (bellek korumasına sahip olmayan) işletim sistemi için güvenlidir ve olmasaydı ciddi bir hata olurdu. Ayrıcalıklı olmayan bir işlem, işletim sisteminin geri kazanamayacağı belleği kalıcı olarak kaybedebilirse, o zaman birçok kez çalıştırmak sistemi belleği yetersiz çalıştırabilir. İşletim sisteminin uzun vadeli sağlığı, imtiyazsız programlardaki hata eksikliğine bağlı olamaz. (Tabii ki, bu sadece en iyi takas alanı ile desteklenen Unix paylaşılan bellek bölümleri gibi şeyleri görmezden geliyor.)
Peter Cordes

7
@GrandOpener Bu durumda, bu ağaç için bir tür bölge temelli tahsisatçı kullanmayı tercih edebilirsiniz, böylece normal bir şekilde tahsis edebilir ve tam bir kesişme ve serbest bırakma yerine tüm bölgeyi bir kerede tahsis edebilirsiniz. yavaş yavaş. Bu hala "uygun".
Thomas,

3
Ek olarak, hafızanın programın tüm ömrü boyunca gerçekten mevcut olması gerekiyorsa, yığında bir yapı oluşturmak gibi mantıklı bir alternatif olabilir, örn main.
Kyle Strand,

11

Bir programın çalışmasının sonunda hafızanın boşaltılması sadece CPU zaman kaybıdır. Bir evi yörüngeden çekmeden önce toplama gibi.

Ancak bazen kısa süren bir program neydi daha uzun süren bir programın parçası haline gelebilir. Sonra eşyaları serbest bırakmak gerekli hale gelir. Bu en azından bir dereceye kadar düşünülmediyse, önemli ölçüde elden geçirmeyi içerebilir.

Bunun akıllıca bir çözümü, bir bellek ayırma yükü oluşturmanıza ve daha sonra hepsini bir çağrı ile atmanıza izin veren "talloc" dur.


1
İşletim sisteminizin kendi sızıntıları olmadığını bildiğinizi varsayalım.
WGroleau

5
"CPU zaman kaybı" Çok, çok az bir zaman olsa da. freegenellikle daha hızlıdır malloc.
Paul Draper,

@PaulDraper Evet, her şeyi geri takas ederken zaman kaybı çok daha önemli.
Deduplicator

5

Çöp toplama olan bir dili kullanabilirsiniz (örneğin, Düzeni, Ocaml, Haskell, Ortak Lisp ve hatta Java, Scala, Clojure).

(Çoğu GC-ed dillerde, orada hiçbir şekilde karşı açıkça ve manuel ! Bazen bazı değerler olabilir boş bellek kesinleşmiş , örneğin GC ve çalıştırma sistemi , ancak bu değildir; bu değer ulaşılamaz olduğunda dosya tanıtıcısı değeri kapatacağını emin, güvenilmez ve bunun yerine , kesinleştirme hiçbir zaman garanti edilmediğinden açıkça dosya işlemcilerinizi kapatmalısınız .

Ayrıca, C (veya hatta C ++) olarak kodlanmış programınız için Boehm'in koruyucu çöp toplayıcısını kullanabilirsiniz . Daha sonra hepsini mallocile değiştirirsiniz GC_malloc ve freeherhangi bir imleci bulmaktan rahatsız olmazsınız . Elbette, Boehm’in GC’sini kullanmanın artılarını ve eksilerini anlamanız gerekir. Ayrıca GC el kitabını okuyun .

Bellek yönetimi bir programın küresel bir özelliğidir. Bir şekilde, (ve belirli bir verinin canlılığı) bütün bir program özelliğinden beri, bileşimsel ve modüler değildir.

Sonunda, diğerleri belirttiği gibi, açıkça freeayrılmış öbek C bellek bölgesini açıkça kullanmak iyi bir uygulamadır. Çok fazla bellek ayırmayan bir oyuncak programı için, hiç freebellek olmamasına bile karar verebilirsiniz ( işlem sona erdiğinde sanal adres alanı da dahil olmak üzere kaynakları işletim sistemi tarafından serbest bırakılacaktır).

Programımın kullanım ömrü boyunca bir parça hafıza kullanmam gerekiyorsa, programın sonlandırılmasından hemen önce boşaltmak gerekli midir?

Hayır, bunu yapmana gerek yok. Ve birçok gerçek dünya programı, tüm kullanım ömrü boyunca ihtiyaç duyulan bir hafızayı boşaltmaktan rahatsız olmaz (özellikle GCC derleyicisi hafızasının bir kısmını boşaltmaz). Bununla birlikte, bunu yaptığınızda (örneğin, freebelirli bir C dinamik parçalı veri parçasını rahatsız etmiyorsanız ), aynı projedeki gelecekteki programcıların çalışmalarını kolaylaştırmak için bu gerçeği yorumlamanız daha iyi olacaktır . Serbest kalan bellek miktarının sınırlı kalmasını ve toplam kullanılan yığın belleğe genellikle nispeten küçük bir ağırlıkta kalmasını öneriyorum.

Sistemin freegenellikle işletim sistemine bellek bırakmadığına dikkat edin (örn . POSIX sistemlerinde munmap (2) yi arayarak ) , ancak genellikle gelecekte kullanılabilecek bir bellek bölgesini işaretleyin malloc. Özellikle, sanal adres alanı (örneğin /proc/self/maps, Linux üzerinden görüldüğü gibi , proc (5) .... 'e bakınız) daha sonra büzülmeyebilir free(bu nedenle , işlemleriniz için aynı miktarda kullanılmış belleği rapor ediyor psya topda rapor ediyor).


3
"Bazen, bazı değerler sonuçlandırılabilir, örneğin GC ve çalışma zamanı sistemi bu değere erişilemediğinde bir dosya tanıtıcısı değerini kapatabilir" Çoğu GCed dilinde, herhangi bir hafızanın geri kazanılacağına dair hiçbir garanti olmadığını vurgulamak isteyebilirsiniz. çıkışta bile çalışacak herhangi bir sonlandırıcı: stackoverflow.com/questions/7880569/…
Deduplicator

Ben şahsen bu cevabı tercih ederim, çünkü bizi GC kullanmaya teşvik ediyor (yani daha otomatik bir yaklaşım).
Ta Thanh Dinh

3
@tathanhdinh Daha otomatik, ancak hafıza için. Diğer tüm kaynaklar için tamamen el kitabı. GC, hafızanın uygun bir şekilde ele alınması için işlem belirleyiciliği ve hafıza ile çalışır. Ve hayır, finalizatörler pek yardımcı olmuyor ve kendi problemleri var.
Deduplicator

3

Bu gerekli değildir , çünkü içinde başarısız olursanız programınızı doğru şekilde yürütemezsiniz. Ancak, eğer bir şans verilirse, seçebileceğiniz sebepler var.

Karşılaştığım en güçlü durumlardan biri (tekrar tekrar), birisinin çalıştırılabilir kodunda çalışan küçük bir simülasyon kodu yazmasıdır. "Bu kodun simülasyona entegre edilmesini istiyoruz" diyorlar. Onlara Monte-Carlo koşusu arasında nasıl yeniden başlamayı planladıklarını soruyorum ve bana boş olarak bakıyorlar. "Yeniden başlatmayı ne demek istiyorsun? Programı yeni ayarlarla mı başlatıyorsun?"

Bazen temiz bir temizleme, yazılımınızın kullanımını çok kolaylaştırır. Pek çok örnekte, hiçbir zaman bir şeyi temizlemeniz gerekmediğini varsayıyorsunuz ve bu varsayımlar çevresindeki verileri ve kullanım ömrünü nasıl idare edebileceğinize dair varsayımlarda bulunuyorsunuz. Bu varsayımların geçerli olmadığı yeni bir ortama taşındığınızda, algoritmaların tümü artık çalışmayabilir.

İşlerin ne kadar tuhaf olabileceğinin bir örneği için, yönetilen dillerin işlemlerin sonunda sonlandırma ile nasıl ilgilendiğine ya da C # 'nın Uygulama Alanlarının programatik olarak durdurulmasıyla nasıl ilgilendiğine bakın. Kendilerini düğüm halinde bağlarlar çünkü bu aşırı durumlarda çatlaklardan düşen varsayımlar vardır.


1

Yine de manuel olarak boş hafıza kullanmadığınız dilleri gözardı etmek ...

Şu an "program" olarak düşündüğünüz şey bir noktada daha büyük bir programın parçası olan bir işlev veya yöntem haline gelebilir. Ve sonra bu işlev birçok kez çağrılabilir. Ve sonra "manuel olarak serbest bırakmanız" gereken bellek, bellek sızıntısı olacaktır. Bu elbette bir yargılama çağrısıdır.


2
bu sadece birkaç saat önce gönderilen en iyi yanıtta yapılan noktayı tekrarladı (ve çok daha iyi açıklandığı gibi) görünüyor : "belleği kullanan ve tahsisat ile birlikte kullanan kodu ayrı bir bileşene taşımak ve daha sonra kullanmak çok daha kolay olabilir belleğin kullanım süresinin, bileşen kullanıcısı tarafından kontrol edilmesi gereken farklı bir bağlamda ... "
gnat

1

Çünkü, bir süre içinde programınızı değiştirmek isteyebilirsiniz, belki de başka bir şeyle bütünleştirin, sırayla veya paralel olarak birkaç örnek çalıştırın. O zaman bu hafızayı elle boşaltmanız gerekecek - ancak koşulları artık hatırlamayacaksınız ve programınızı yeniden anlamak çok daha fazla zaman alacak.

Onları anladığınızda hala taze olan şeyleri yapın.

Gelecekte büyük getiri sağlayabilecek küçük bir yatırım.


2
Bu, önceki 11 cevapta verilen ve açıklanan önemli noktalar üzerinde önemli bir şey eklemek gibi görünmüyor. Özellikle, yaklaşık muhtemelen zaten üç ya da dört kez yapıldıktan gelecekte programda değişiklik işaret
sivrisinek

1
@gnat Kıvrımlı ve gizlenmiş şekillerde - evet. Açık bir ifadeyle - hayır. Hızlılığa değil cevap kalitesine odaklanalım.
Agent_L

1
bilge, en iyi cevap bu noktayı açıklamakta çok daha iyi görünüyor
gnat

1

Hayır, gerekli değil, ama bu iyi bir fikir.

Sen hissediyorum demek "Ben bunu kullanarak işim bittikten sonra ben boş hafıza yapsam gizemli ve korkunç şeyler olur."

Teknik açıdan, bunun tek sonucu, programınızın zor bir sınıra ulaşılana kadar (örneğin, sanal adres alanınız tükenene) veya performans kabul edilemez hale gelinceye kadar daha fazla bellek yemeye devam etmesidir. Program çıkmak üzereyse, bunların hiçbiri önemli değildir çünkü süreç etkili bir şekilde sona ermektedir. "Gizemli ve korkunç şeyler" tamamen geliştiricinin zihinsel durumu ile ilgilidir. Bir bellek sızıntısının kaynağını bulmak mutlak bir kabus olabilir (bu yetersiz bir ifadedir) ve sızıntısız kod yazmak için çok fazla beceri ve disiplin gerekir. Bu beceri ve disiplini geliştirmek için önerilen yaklaşım , program bitmek üzere olsa bile, artık gerekli olmadığında daima belleği boşaltmaktır.

Elbette bu, başkalarının söylediği gibi, kodunuzun yeniden kullanılması ve daha kolay adapte edilebilmesi avantajına sahiptir.

Ancak , daha iyidir en azından bir durum var değil sadece programı sonlandırma önce hafızayı boşaltmak için.

Milyonlarca küçük tahsisat yaptığınız durumu düşünün ve bunlar çoğunlukla diske aktarıldı. Her şeyi serbest bırakmaya başladığınızda, hafızanızın çoğunun RAM'e geri gönderilmesi gerekir, böylece defter tutma bilgilerine yalnızca verileri hemen atmak için erişilebilir. Bu program sadece çıkmak için birkaç dakika sürebilir! Bahsetmiyorum bile, bu süre zarfında diske ve fiziksel belleğe çok fazla baskı yap. Fiziksel bellek başlangıçta yetersiz kalıyorsa (belki de program kapatılıyor çünkü başka bir program çok fazla bellek çiğniyordur), o zaman birkaç nesnenin serbest bırakılması gerektiğinde, tek tek sayfaların birkaç kez girip çıkması gerekebilir. Aynı sayfa, ancak art arda serbest bırakılmaz.

Bunun yerine program sadece iptal edilirse, işletim sistemi diske yerleştirilmiş olan tüm belleği atmayacaktır; bu disk neredeyse hiç anlık değildir, çünkü herhangi bir disk erişimi gerektirmez.

Bir OO dilinde, bir nesnenin yıkıcısını çağırmanın aynı zamanda hafızayı değiştirilmeye zorlayacağına dikkat etmek önemlidir; Bunu yapmak zorunda kalırsanız, hafızayı boşaltabilirsiniz.


1
Disk belleği belleğinin serbest bırakılması için çağrı yapılması gerektiği fikrini destekleyecek bir şey söyleyebilir misiniz? Bu açıkçası verimsiz, elbette modern işletim sistemleri bundan daha akıllı!

1
@ Tüm Ticaretin Sonunda, modern işletim sistemleri akıllıdır, ancak ironik bir şekilde çoğu modern dil değildir. Özgür olduğunuzda (bir şey), derleyici yığına "bir şey" koyar, sonra ücretsiz çağırır (). Yığına koymak, değiştirilirse tekrar RAM'e değiştirilmesini gerektirir.
Jeffiekins

@JonofAllTrades ücretsiz bir liste bu etkiye sahip olurdu, ama oldukça eski bir malloc uygulaması. Ancak işletim sistemi böyle bir uygulamayı kullanmanızı engelleyemez.

0

Manuel olarak temizlemenin ana nedenleri şunlardır: Çıkışta temizlenecek olan bir bellek bloğu için gerçek bir bellek sızıntısına neden olma ihtimaliniz daha düşüktür, bazen sadece tüm veri yapısını yürürken tahsis edicide hataları yakalarsınız bunu ayırması ve hiç bir nesne böylece planı ayrı eğer çok daha küçük bir baş ağrısı olacak gelmez programı çıkmadan önce ayırmanın almak gerekir.

Manuel olarak temizlenmemenin ana nedenleri: performans, performans, performans ve gereksiz temizleme kodunuzda bir çarpışma hatasına veya güvenceye dönüşebilecek gereksiz temizleme kodunda bir veya iki kez serbest bırakma olasılığı olabilir. istismar.

Performansı önemsiyorsanız, tahmin etmek yerine tüm zamanınızı nereye harcadığınızı bulmak için her zaman profil oluşturmak istersiniz. Olası bir uzlaşma, isteğe bağlı temizleme kodunuzu koşullu bir bloğa sarmak, hata ayıklama sırasında bırakmanız, böylece kodu yazma ve hata ayıklama avantajlarından faydalanmanız ve ardından yalnızca ampirik olarak çok fazla belirlediğiniz takdirde ek olarak derleyiciye onu son çalıştırılabilirinizde atlamasını söyleyin. Ve programın kapatılmasındaki bir gecikme, neredeyse tanımı gereği, neredeyse hiç kritik yolda değildir.

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.