2012-05-09 28 views
14

KodDelegeler bellek sızıntısına neden olabilir mi? (Doğru) GC.TotalMemory çıkışı böylece

using System; 
internal static class Test 
{ 
    private static void Main() 
    { 
     try 
     { 
      Console.WriteLine("{0,10}: Start point", GC.GetTotalMemory(true)); 
      Action simpleDelegate = SimpleDelegate; 
      Console.WriteLine("{0,10}: Simple delegate created", GC.GetTotalMemory(true)); 
      Action simpleCombinedDelegate = simpleDelegate + simpleDelegate + simpleDelegate; 
      Console.WriteLine("{0,10}: Simple combined delegate created", GC.GetTotalMemory(true)); 
      byte[] bigManagedResource = new byte[100000000]; 
      Console.WriteLine("{0,10}: Big managed resource created", GC.GetTotalMemory(true)); 
      Action bigManagedResourceDelegate = bigManagedResource.BigManagedResourceDelegate; 
      Console.WriteLine("{0,10}: Big managed resource delegate created", GC.GetTotalMemory(true)); 
      Action bigCombinedDelegate = simpleCombinedDelegate + bigManagedResourceDelegate; 
      Console.WriteLine("{0,10}: Big combined delegate created", GC.GetTotalMemory(true)); 
      GC.KeepAlive(bigManagedResource); 
      bigManagedResource = null; 
      GC.KeepAlive(bigManagedResourceDelegate); 
      bigManagedResourceDelegate = null; 
      GC.KeepAlive(bigCombinedDelegate); 
      bigCombinedDelegate = null; 
      Console.WriteLine("{0,10}: Big managed resource, big managed resource delegate and big combined delegate removed, but memory not freed", GC.GetTotalMemory(true)); 
      GC.KeepAlive(simpleCombinedDelegate); 
      simpleCombinedDelegate = null; 
      Console.WriteLine("{0,10}: Simple combined delegate removed, memory freed, at last", GC.GetTotalMemory(true)); 
      GC.KeepAlive(simpleDelegate); 
      simpleDelegate = null; 
      Console.WriteLine("{0,10}: Simple delegate removed", GC.GetTotalMemory(true)); 
     } 
     catch (Exception e) 
     { 
      Console.WriteLine(e); 
     } 
     Console.ReadKey(true); 
    } 
    private static void SimpleDelegate() { } 
    private static void BigManagedResourceDelegate(this byte[] array) { } 
} 

GC.TotalMemory(true) 
    105776: Start point 
    191264: Simple delegate created 
    191328: Simple combined delegate created 
100191344: Big managed resource created 
100191780: Big managed resource delegate created 
100191812: Big combined delegate created 
100191780: Big managed resource, big managed resource delegate and big combined delegate removed, but memory not freed 
    191668: Simple combined delegate removed, memory freed, at last 
    191636: Simple delegate removed 
+0

Yürütülebilir dosya için teşekkürler, btw! – usr

cevap

17

İlginç durumda olduğunu gösteriyor. İşte çözümü:

enter image description here

birleştiren delegeler gözlemsel olarak saf geçerli: delegeler dışarıya iletmenin gibi görünüyor. Ancak, dahili olarak, varolan delegeler değiştirilmektedir. Bazı nedenlerle, performans nedenleriyle aynı _invocationList paylaşımını paylaşırlar (aynı etkinliğe birkaç delegenin bağlandığı senaryoyu optimize ederler). Ne yazık ki, simpleCombinedDelegate için _invocationList, belleğin canlı kalmasına neden olan bigMgdResDelegate referansıdır.

+1

Resimlerle ve her şeyle mükemmel cevap. –

+0

Vay, bu şaşırtıcı! Ama gerçek mutasyon mu, yoksa akıllı bir yerel optimizasyon mu? Belki de derleyici/JIT yöntemde ileriye bakar ve ilk delege oluşturmadan önce bir kez daha önceden belirlenmiş 4 bileşenli bir dizi oluşturur mu? – Weeble

+0

@Weeble, JIT optimizasyonu ile ilgili ne demek istediğinizden emin değilsiniz, ancak JIT genellikle oldukça aptaldır. Böyle karmaşık şeyler yapmaz. Her neyse, JIT yalnızca IL kodu sipariş ettiğinde işleri değiştirebilir. * Asla * mutasyonun kendisini tanıtır çünkü bu güvenli olmaz. – usr

1

Buradaki noktayı kaçırıyor olabilirim, ancak çöp toplama tasarımı deterministik değil. Ergo, belleği geri aldığında karar vermek için .NET çerçevesine kalmış.

GC.GetTotalMemory'yi basit bir döngüde çalıştırabilir ve farklı şekiller elde edebilirsiniz. Belki de dokümantasyonun iade edilen rakamın bir yakınsama olduğunu belirlemesi sürpriz değildir.

+0

Öncelikle bu cevabın kabul edilmesi, çünkü OP'nin önerdiği test, yararlı bir şeyin çoğunu tespit etmek için çok basittir. Yönetilen ortamlardaki 'sızdırılmış bellek', genellikle kötü tasarlanmış bir kodla (şeylerin listelerine referans veren durağan nesneler) geri döner. Oyunda bu noktada GC'de herhangi bir gerçek hata bulmanız pek olası değildir. – Sprague

+0

GetTotalMemory, açık bir şekilde GC yapar. Bu test oldukça güvenilirdir. Ayrıca, bu sınamayı geçersiz kılan değişkenlerin bu sınamadaki diğer tüm durumlarda da çalıştığını (bunun beklendiği gibi çalıştığını gösteren ek kanıt) dikkat edin. – usr

+0

OP'nin amacı, “GC.GetTotalMemory (true)” un bir koleksiyonu zorlaması gerektiğine inanıyor, ancak hem büyük delegeleri hem de bir koleksiyonu zorladıktan sonra bellek hala dağıtılıyor. –

İlgili konular