2012-05-09 10 views
14

コードデリゲートがメモリリークを引き起こす可能性がありますか? GC.TotalMemory(真)はそう

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

実行可能ファイルのおかげで、btw! – usr

答えて

17

興味深いケースを示しているようです。ここでは解決されています。デリゲートを組み合わせる

enter image description here

observationally純粋です:デリゲートが外部に不変であるように見えます。しかし内部では、既存の代理人は変更されています。特定の条件下では、同じ理由で同じパフォーマンスの理由で同じ_invocationListを共有します(同じイベントに少数のデリゲートが参加するというシナリオでは最適化されます)。残念ながら、simpleCombinedDelegate_invocationListは、bigMgdResDelegateを参照するため、メモリが有効に保たれます。

+1

画像やすべての優れた答え。 –

+0

うわー、それは驚くべきことです!しかし、真の突然変異か、賢明な局所最適化ですか?おそらく、コンパイラ/ JITは最初のデリゲートを作成する前に、メソッドに先を見て事前に配置された4要素配列を作成しますか? – Weeble

+0

@Weeble、JIT最適化に関して何を意味するのかはっきりしていませんが、JITは通常かなり馬鹿です。それはそのような洗練されたものをしません。とにかく、JITは、ILコードがそれを注文したときにだけ、物を突然変異させることができます。それは危険ではないので、突然変異そのものを導入することは決してありません。 – usr

1

ここではポイントが不足している可能性がありますが、ガベージコレクションは設計上は非決定的です。 Ergoでは、いつメモリを再利用するかを決めるのは.NETフレームワークに任されています。

GC.GetTotalMemoryを単純なループで実行して、異なる数字を得ることができます。ドキュメンテーションで返される数字が近似値であると指定されているので、おそらく驚きはありません。

+0

この回答に同意する主な点は、OPが提案するテストが有用であるものの多くを検出するには単純すぎるためです。管理された環境では、「リークされたメモリ」は通常、不適切な設計コード(物事のリストへの参照を保持する静的オブジェクト)につながります。ゲームのこの時点で実際のバグを見つけることはほとんどありません。 – Sprague

+0

GetTotalMemoryは明示的なGCを実行します。このテストはかなり信頼できます。また、このテストでは他のすべてのケースで変数を無効にすることができます(これが期待どおりに機能することのさらなる証拠)。 – usr

+0

私はOPのポイントは 'GC.GetTotalMemory(true)'はコレクションを強制すると思われますが、大きなデリゲートとコレクションを強制的にnullにした後も、メモリは割り当てられているということです。 –

関連する問題