2013-04-18 13 views
10

.NETで型がガベージコレクションされていないことはよく知られています。つまり、f.exで遊んでいる場合は、 Reflection.Emit、あなたはAppDomainsをアンロードするように注意しなければなりません。少なくとも、それは私が物事の仕組みを理解するために使った方法です。MakeGenericType /ジェネリック型はガベージコレクションされていますか?

genericタイプであるかどうか疑問に思っていました。具体的には、MakeGenericTypeで作成されたジェネリックは、たとえばユーザー入力に基づいています。 :-)

だから私は、次のテストケースを構築:あなたが見ることができるように

public interface IRecursiveClass 
{ 
    int Calculate(); 
} 

public class RecursiveClass1<T> : IRecursiveClass 
            where T : IRecursiveClass,new() 
{ 
    public int Calculate() 
    { 
     return new T().Calculate() + 1; 
    } 
} 
public class RecursiveClass2<T> : IRecursiveClass 
            where T : IRecursiveClass,new() 
{ 
    public int Calculate() 
    { 
     return new T().Calculate() + 2; 
    } 
} 

public class TailClass : IRecursiveClass 
{ 
    public int Calculate() 
    { 
     return 0; 
    } 
} 

class RecursiveGenericsTest 
{ 
    public static int CalculateFromUserInput(string str) 
    { 
     Type tail = typeof(TailClass); 
     foreach (char c in str) 
     { 
      if (c == 0) 
      { 
       tail = typeof(RecursiveClass1<>).MakeGenericType(tail); 
      } 
      else 
      { 
       tail = typeof(RecursiveClass2<>).MakeGenericType(tail); 
      } 
     } 
     IRecursiveClass cl = (IRecursiveClass)Activator.CreateInstance(tail); 
     return cl.Calculate(); 
    } 

    static long MemoryUsage 
    { 
     get 
     { 
      GC.Collect(GC.MaxGeneration); 
      GC.WaitForFullGCComplete(); 
      return GC.GetTotalMemory(true); 
     } 
    } 

    static void Main(string[] args) 
    { 
     long start = MemoryUsage; 

     int total = 0; 
     for (int i = 0; i < 1000000; ++i) 
     { 
      StringBuilder sb = new StringBuilder(); 
      int j = i; 
      for (int k = 0; k < 20; ++k) // fix the recursion depth 
      { 
       if ((j & 1) == 1) 
       { 
        sb.Append('1'); 
       } 
       else 
       { 
        sb.Append('0'); 
       } 
       j >>= 1; 
      } 

      total += CalculateFromUserInput(sb.ToString()); 

      if ((i % 10000) == 0) 
      { 
       Console.WriteLine("Current memory usage @ {0}: {1}", 
            i, MemoryUsage - start); 
      } 
     } 

     Console.WriteLine("Done and the total is {0}", total); 
     Console.WriteLine("Current memory usage: {0}", MemoryUsage - start); 

     Console.ReadLine(); 
    } 
} 

を、一般的なタイプは再帰の終わりを示す「テール」クラスで、「おそらく再帰的な」が定義されています。そして、GC.TotalMemoryUsageが不正行為ではないことを確認するために、私もタスクマネージャを開いた。

これまでのところとても良いです。私がした次のことは、この獣を撃ち上げることでした。そして、私は 'Out of memory'を待っていました。私はそれが私の期待に反することに気付きました。は、実際には、時間の経過とともにメモリー消費量がわずかに低下します。

誰か説明していただけますか?ジェネリック型は実際にGCによって収集されますか?もしそうなら...反射もありますか?ガベージコレクションはどうですか?

+0

あなたは 'Target'は、GCのパスを実行した後にnullであるかどうかを確認、その後、あなたの作成したジェネリック型に' WeakReference'を作成することによって、実験ができます。 –

+0

これは、[ジェネリックスはJITコンパイラによってどのようにコンパイルされるのですか?](http://stackoverflow.com/questions/5342345/how-do-generics-get-compiled-by-the-jit-compiler) –

答えて

19

最初の質問にお答えします。

タイプの一般的な構造は収集されません。

ただし、C<string>C<object>を作成すると、CLRは実際にのコードに対してコードを1回だけ生成します。文字列への参照とオブジェクトへの参照が同じサイズであることが保証されているので、安全に行うことができます。それはかなり賢いです。 C<int>C<double>を作成すると、メソッドのコードが各構造に対して1回ずつ2回生成されます。 (メソッドのコードはもちろんの全てで発生したと仮定すると、方法は、オンデマンドでJITコンパイルされている。だからこそ、その呼ばjitting。)

ジェネリック型が収集されていないことを実証するために、代わりにジェネリック型を作成

class C<T> { public static readonly T Big = new T[10000]; } 

C<object>およびC<string>は、メソッドに対して生成されたコードを共有しますが、それぞれ独自の静的フィールドを取得し、これらのフィールドは永遠に存続します。より多くのタイプを構築するほど、それらの大きな配列でいっぱいになるでしょう。

これで、これらのタイプを収集できない理由を知ることができます。将来誰かがいつでもそれらの配列のメンバーにアクセスしようとしているかどうかを知る方法はありません。最後の配列へのアクセスがいつ行われるかわからないので、永遠に生きなければならないので、それを含む型も永遠に生きなければなりません。


2番目の質問に答えるには:ダイナミックに放射されたアセンブリを収集する方法はありますか?

はい。ドキュメントはここにある:

http://msdn.microsoft.com/en-us/library/dd554932.aspx

+0

@ ErikLippertうわー、それは素晴らしい答えです。エリック、ありがとう!私は静的なものについて考え出したので、クラスに静的を追加しなかったのです。私はまだ線について考えていますが、実際にはメソッドのコードは実際には一度しか生成されません[..]。これはまた、静的なものを持たない2つの参照型ジェネリッククラスを持つ場合、内部的に1つの '型'しかメモリに存在しないことを意味しますか? (つまり、タイプは単なるコードであり、メモリは1回しか作成されないため)JIT最適化の境界も意味します(TがFooとBarのどちらでもコードのインスタンスが1つしかない場合、インライン展開は不可能です)。 – atlaste

+0

'string GetTypeName (T param){return typeof(T).ToString();}'のようなメソッドのコードの1つのコピーが、すべてのクラス型 'T'で共有される場合、コードはどのような型それはと呼ばれる?それは何とか隠されたパラメータとして渡されますか? – supercat

+3

@supercat:Magic! –

関連する問題