“Bellek Sızıntısı” anatomisi


172

.NET perspektifinde:

  • Bir nedir bellek sızıntısı ?
  • Başvurunuzun sızdırıp sızdırmadığını nasıl belirleyebilirsiniz? Etkileri nelerdir?
  • Bellek sızıntısını nasıl önleyebilirsiniz?
  • Uygulamanızda bellek sızıntısı varsa, işlem sonlandığında veya öldürüldüğünde ortadan kalkar mı? Veya uygulamanızdaki bellek sızıntıları, işlem tamamlandıktan sonra bile sistemdeki diğer işlemleri etkiler mi?
  • Peki ya COM Interop ve / veya P / Invoke üzerinden erişilen yönetilmeyen kodlar?

Yanıtlar:


110

Gördüğüm en iyi açıklama , Programlamanın Temelleri e-kitabının 7. Bölümünde .

Temel olarak, .NET'te başvurulan nesneler köklendiğinde bellek sızıntısı oluşur ve bu nedenle çöp toplanamaz. Bu, amaçlanan kapsamın ötesindeki referansları beklediğinizde yanlışlıkla gerçekleşir.

OutOfMemoryExceptions almaya başladığınızda sızıntılarınız olduğunu veya bellek kullanımınızın beklediğinizin ötesine geçtiğini göreceksiniz ( PerfMon'un güzel bellek sayaçları vardır).

.NET'in bellek modelini anlamak , bundan kaçınmanın en iyi yoludur. Özellikle, çöp toplayıcının nasıl çalıştığını ve referansların nasıl çalıştığını anlamak - sizi e-kitabın 7. bölümüne yönlendiriyorum. Ayrıca, muhtemelen en yaygın olay olan yaygın tuzaklara dikkat edin. Nesne Eğer bir nesne üzerinde bir olaya kayıtlı olduğu B , daha sonra nesne A nesnesi kadar etrafında dolanır B çünkü kaybolur B başvuru tutan A . Çözüm, işiniz bittiğinde etkinliklerinizin kaydını silmektir.

Tabii ki, iyi bir bellek profili, nesne grafiklerinizi görmenize ve referansların nereden geldiğini ve hangi kök nesnenin sorumlu olduğunu görmek için nesnelerinizin yerleştirilmesini / referansını keşfetmenizi sağlar ( kırmızı kapı karınca profili , JetBrains dotMemory, memprofiler gerçekten iyidir veya salt metin WinDbg ve SOS kullanabilirsiniz , ancak gerçek bir guru değilseniz ticari / görsel bir ürün şiddetle tavsiye ederim).

Paylaşılan referansların çöp toplayıcı tarafından yönetilmesi dışında, yönetilmeyen kodun tipik bellek sızıntılarına maruz kaldığına inanıyorum. Bu son nokta hakkında yanılmış olabilirim.


11
Oh, kitap sever misin? Yazarın zaman zaman stackoverflow üzerinde açıldığını gördüm.
Johnno Nolan

Bazı .NET nesneleri de kendilerini köklendirebilir ve toplanamaz hale gelebilir. Tek kullanımlık olan her şey bu nedenle bertaraf edilmelidir.
kyoryu

1
@kyoryu: Bir nesne kendisini nasıl köklendirir?
Andrei Rînea

2
@Andrei: Sanırım çalışan bir iş parçacığı belki de kendisini köklendiren bir nesnenin en iyi örneğidir. Herkese açık statik olmayan bir konuma referans veren bir nesne (statik bir olaya abone olmak veya statik alan başlatma yoluyla bir singleton uygulamak gibi), aynı zamanda ... ... demirlemesinden "kökünden sök".
Jeffrey Hantin

@ Jeffry bu ne olduğunu açıklamak için mantıksız bir yoldur ve hoşuma gitti!
Exitos

35

Kesin olarak, bir bellek sızıntısı program tarafından "artık kullanılmayan" bellek tüketiyor.

"Artık kullanılmıyor" ifadesinin birden fazla anlamı vardır, "ona daha fazla referans yok" anlamına gelebilir, yani tamamen kurtarılamaz veya referans alınabilir, kurtarılabilir, kullanılmamış anlamına gelebilir, ancak program yine de referansları tutar. Mükemmel yönetilen nesneler için yalnızca .Net için geçerlidir . Ancak, tüm sınıflar mükemmel değildir ve bir noktada temelde yönetilmeyen bir uygulama bu süreç için kaynakları kalıcı olarak sızdırabilir.

Her durumda, uygulama kesinlikle gerekenden daha fazla bellek tüketir. Sızan miktarlara bağlı olarak yan etkiler, hiçbirinden aşırı toplama nedeniyle oluşan yavaşlamaya, bir dizi bellek istisnasına ve son olarak ölümcül bir hataya ve ardından zorla işlem sonlandırmasına gidebilir.

İzleme, işleminize daha fazla belleğin ayrıldığını gösterdiğinde bir uygulamanın bellek sorunu olduğunu biliyorsunuz her çöp toplama döngüsünden sonra . Bu durumda, ya çok fazla bellek tutuyorsunuz ya da temelde yönetilmeyen bazı uygulamalar sızdırıyor.

Çoğu sızıntı için, işlem sonlandırıldığında kaynaklar kurtarılır, ancak bazı kesin durumlarda bazı kaynaklar her zaman kurtarılamaz, GDI imleç tanıtıcıları bununla meşhurdur. Tabii ki, bir süreçler arası iletişim mekanizmanız varsa, diğer süreçte ayrılan bellek, süreç serbest kalana veya sona erene kadar serbest bırakılmaz.


32

"Bellek sızıntısı nedir" ve "etkileri nelerdir" sorularının zaten iyi yanıtlandığını düşünüyorum, ancak diğer sorulara birkaç şey daha eklemek istedim ...

Uygulamanızın sızdırıp sızdırmadığını anlama

İlginç bir yol, her durumda sadece işleminize bakarak, tüm yığınlarda ve # Gen 2 koleksiyonlarında # bayt için perfmon açmak ve izler eklemektir . Belirli bir özelliğin kullanılması toplam baytların artmasına neden oluyorsa ve bu bellek bir sonraki Gen 2 koleksiyonundan sonra ayrılmış durumda kalıyorsa, özelliğin belleğe sızdığını söyleyebilirsiniz.

Nasıl önlenir

Başka iyi görüşler de verilmiştir. .NET bellek sızıntılarının belki de en çok gözden kaçan nedeninin, nesnelere bunları kaldırmadan olay işleyicileri eklemektir. Bir nesneye bağlı bir olay işleyicisi, o nesneye bir başvuru şeklidir, bu nedenle diğer tüm referanslar gittikten sonra bile toplanmasını önler. Her zaman olay işleyicilerini ayırmayı unutmayın ( -=C #'daki sözdizimini kullanarak ).

İşlem bittiğinde sızıntı kayboluyor mu ve COM birlikte çalışmasına ne dersiniz?

İşleminiz sona erdiğinde, adres alanına eşlenen tüm bellekler, DLL'lerden sunulan COM nesneleri de dahil olmak üzere işletim sistemi tarafından geri kazanılır. Nispeten nadiren, COM nesneleri ayrı işlemlerden sunulabilir. Bu durumda, işleminiz sona erdiğinde, kullandığınız herhangi bir COM sunucusu işleminde ayrılan bellekten yine de sorumlu olabilirsiniz.


19

Bellek sızıntılarını, tamamlandıktan sonra ayrılan tüm belleği serbest bırakmayan bir nesne olarak tanımlarım. Windows API ve COM (yani içinde bir hata var ya da doğru yönetilmiyor yönetilmeyen kod), çerçeve ve üçüncü taraf bileşenleri kullanıyorsanız, bu uygulamanızda olabileceğini buldum. Ayrıca kalemler gibi bazı nesneleri kullandıktan sonra kadar gelgit değil bulduk soruna neden olabilir.

Şahsen ben neden olabilir ama nokta net uygulamalarda bellek sızıntıları hariç Bellek Yetersiz İstisnalar yaşadım. (OOM da iğnelemeden gelebilir bkz. Sabitleme ARTICAL ). OOM hataları almıyorsanız veya buna neden olan bir bellek sızıntısı olup olmadığını onaylamanız gerekiyorsa, tek yol uygulamanızı profillemektir.

Ben de denemek ve aşağıdakileri sağlamak:

a) Tek kullanımlık olan her şey, nihayet bir blok veya fırçalar, kalemler vb.

b) Kapatma yöntemi olan herhangi bir şey, nihayet veya using deyimi kullanılarak tekrar kapatılır.

c) Bunların daha sonra doğru bir şekilde ele alındığı yönetilmeyen kod / windows API'leri kullanıyorsanız. (bazılarının kaynakları serbest bırakmak için temizleme yöntemleri vardır)

Bu yardımcı olur umarım.


19

.NET'te bellek sızıntısı teşhisi yapmanız gerekiyorsa şu bağlantıları kontrol edin:

http://msdn.microsoft.com/en-us/magazine/cc163833.aspx

http://msdn.microsoft.com/en-us/magazine/cc164138.aspx

Bu makaleler, işleminizin bir bellek dökümü nasıl oluşturulacağını ve nasıl analiz edileceğini açıklar, böylece ilk önce sızıntınızın yönetilip yönetilmediğini ve yönetilip yönetilmediğini, nereden geldiğini nasıl anlayacağınızı belirleyebilirsiniz.

Microsoft ayrıca, DebugDiag adlı ADPlus'ın yerine çökme dökümlerinin oluşturulmasına yardımcı olacak daha yeni bir araca sahiptir.

http://www.microsoft.com/downloads/details.aspx?FamilyID=28bd5941-c458-46f1-b24d-f60151d875a3&displaylang=en



15

Çöp toplayıcının nasıl çalıştığına dair en iyi açıklama Jeff Richters CLR'de C # kitabı, (Bölüm 20). Bunu okumak, nesnelerin nasıl devam ettiğini anlamak için harika bir temel sağlar.

Nesneleri yanlışlıkla köklendirmenin en yaygın nedenlerinden biri, bir sınıfın dışındaki olayları bağlamaktır. Harici bir olay bağlarsanız

Örneğin

SomeExternalClass.Changed += new EventHandler(HandleIt);

ve attığınız zaman kancayı çıkarmayı unutun, o zaman SomeExternalClass sınıfınıza bir ref sağlar.

Yukarıda belirtildiği gibi, SciTech bellek profili oluşturucusu , sızdığından şüphelenilen nesnelerin köklerini göstermede mükemmeldir.

Ancak, belirli bir türün sadece WnDBG kullanmasını kontrol etmenin çok hızlı bir yolu da vardır (bunu ekli iken VS.NET anlık penceresinde bile kullanabilirsiniz):

.loadby sos mscorwks
!dumpheap -stat -type <TypeName>

Şimdi bu türdeki nesneleri atacağınızı düşündüğünüz bir şey yapın (örneğin, bir pencereyi kapatın). Burada System.GC.Collect()birkaç kez çalışacak bir yerde bir hata ayıklama düğmesi olması kullanışlı .

Sonra !dumpheap -stat -type <TypeName>tekrar çalıştırın . Sayı düşmediyse veya beklediğiniz kadar düşmediyse, daha fazla araştırma için bir temeliniz var. (Bu ipucunu Ingo Rammer tarafından verilen bir seminerden aldım ).


14

Sanırım yönetilen bir ortamda, bir sızıntı, etraftaki büyük bir bellek yığınına gereksiz bir referans tutmak olurdu.


11

Neden insanlar .NET'te bellek sızıntısının diğer sızıntılarla aynı olmadığını düşünüyor?

Bellek sızıntısı, bir kaynağa eklediğinizde ve gitmesine izin vermediğiniz zamandır. Bunu hem yönetilen hem de yönetilmeyen kodlama ile yapabilirsiniz.

.NET ve diğer programlama araçları ile ilgili olarak, çöp toplama ve uygulamanızın sızmasına neden olacak durumları en aza indirmenin diğer yolları hakkında fikirler vardır. Ancak bellek sızıntılarını önlemenin en iyi yöntemi, kullandığınız platformda temeldeki bellek modelinizi ve işlerin nasıl çalıştığını anlamanız gerektiğidir.

GC ve diğer sihirlerin karışıklığınızı temizleyeceğine inanmak, bellek sızıntılarının kısa yoludur ve daha sonra bulmak zor olacaktır.

Yönetilmeyen kodlama yaparken, normalde temizlediğinizden emin olursunuz, tuttuğunuz kaynakların temizlikçinin değil, temizlik sorumluluğunuz olacağını bilirsiniz.

Öte yandan .NET'te, birçok insan GC'nin her şeyi temizleyeceğini düşünüyor. Eh, sizin için bir şeyler yapar, ancak bunun olduğundan emin olmanız gerekir. .NET birçok şeyi sarar, bu nedenle yönetilen veya yönetilmeyen bir kaynakla uğraşıp uğraşmadığınızı her zaman bilmezsiniz ve ne ile uğraştığınızdan emin olmanız gerekir. Yazı tiplerini, GDI kaynaklarını, etkin dizini, veritabanlarını vb. Kullanmak genellikle dikkat etmeniz gereken şeylerdir.

Yönetilen terimlerle, süreç öldürüldükten / çıkarıldıktan sonra gideceğini söylemek için boynumu sıraya koyacağım.

Buna rağmen birçok insan bunu görüyor ve umarım bu bitecek. Kullanıcının dağınıklığınızı temizlemek için uygulamanızı sonlandırmasını isteyemezsiniz! IE, FF vb. Olabilen bir tarayıcıya bakın, ardından Google Reader'ı açın, birkaç gün kalmasına izin verin ve ne olduğuna bakın.

Daha sonra tarayıcıda başka bir sekme açarsanız, bir siteye gidin, ardından tarayıcı sızıntısını yapan diğer sayfayı barındıran sekmeyi kapatın, tarayıcının belleği serbest bırakacağını düşünüyor musunuz? IE ile öyle değil. Google Reader'ı kullanırsam bilgisayarımda IE kısa sürede (yaklaşık 3-4 gün) 1 GiB belleği kolayca yiyecektir. Bazı haber sayfaları daha da kötüdür.


10

Sanırım yönetilen bir ortamda, bir sızıntı, etraftaki büyük bir bellek yığınına gereksiz bir referans tutmak olurdu.

Kesinlikle. Ayrıca, uygun olduğunda tek kullanımlık nesneler üzerinde .Dispose () yönteminin kullanılmaması bellek sızıntılarına neden olabilir. Bunu yapmanın en kolay yolu, sonunda .Dispose () öğesini otomatik olarak yürüttüğü için bir kullanım bloğudur:

StreamReader sr;
using(sr = new StreamReader("somefile.txt"))
{
    //do some stuff
}

Ve yönetilmeyen nesneler kullanan bir sınıf oluşturursanız, IDisposable'ı doğru bir şekilde uygulamıyorsanız, sınıfınızın kullanıcıları için bellek sızıntılarına neden olabilirsiniz.


9

Tüm bellek sızıntıları program sonlandırılarak giderilir.

Yeterli bellek sızıntısı varsa İşletim Sistemi sorunu sizin adınıza çözmeye karar verebilir.


8

.Net'te bir mem sızıntısının ne olacağı konusunda.

Uygulamanızın bellek kullanımını görmek için profil oluşturabilir ve olmaması gerektiğinde çok fazla bellek yönetiyorsa sızıntı olduğunu söyleyebilirsiniz.

Yönetilen terimlerle, süreç öldürüldükten / çıkarıldıktan sonra gideceğini söylemek için boynumu sıraya koyacağım.

Yönetilmeyen kod kendi canavarıdır ve içinde bir sızıntı varsa, standart bir mem takip edecektir. kaçak tanımı.


7

Ayrıca, .NET'in biri büyük nesne yığını olan iki yığını olduğunu unutmayın. Yaklaşık 85 bin veya daha büyük nesnelerin bu yığın üzerine konulduğuna inanıyorum. Bu yığın, normal yığından farklı bir ömür boyu kurallara sahiptir.

Büyük bellek yapıları (Sözlük veya Liste) oluşturuyorsanız, kesin kuralların ne olduğuna bakmak akıllıca olur.

Win98 veya eşdeğerlerini çalıştırmadığınız sürece, işlem sonlandırmadaki hafızayı geri kazanmak kadar, her şey sonlandırma sırasında işletim sistemine geri gönderilir. Tek istisna süreçler arası açılan şeylerdir ve başka bir süreç hala kaynağı açıktır.

COM Nesneleri tho zor olabilir. IDisposeDeseni her zaman kullanırsanız, güvende olacaksınız. Ama uygulayan birkaç birlikte çalışma derlemesiyle karşılaştım IDispose. Buradaki anahtar Marshal.ReleaseCOMObject, işiniz bittiğinde aramaktır. COM nesneleri hala standart COM başvuru sayma kullanır.


6

Buldum NET bellek Profiler Net bellek sızıntılarını bulma zaman çok iyi bir yardım. Microsoft CLR Profiler gibi ücretsiz değil, ama bence noktaya göre daha hızlı ve daha fazlası. bir


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.