ガベージコレクター、Unity、および弱いリンクについて

私のプロジェクトには、特定のIT



およびタイプのファクトリインターフェースメソッドがあります。



 interface IT {} public IT CreateT(IA a, IB b, IC c, Type concreteType) { //  ,     concreteType,      . }
      
      







IT



を実装するクラスには、タイプIA



IB



またはIC



のオブジェクトの組み合わせを受け入れるさまざまなシグネチャを持つコンストラクターがあります。したがって、 IT



実装の数が増えると、作成のためのコードの臭いがますます強くなり、最終的に破棄して置き換えることが決定されましたUnityを使用した簡単なコード、次のようなもの:



 private static IT CreateITInternal(IA a, Type targetType) { using (UnityContainer cont = new UnityContainer()) { cont.RegisterInstance<IA>(a, new ExternallyControlledLifetimeManager()); cont.RegisterType(typeof(IT), targetType, new ExternallyControlledLifetimeManager()); return cont.Resolve<IT>(); } }
      
      





(簡単にするために、コードを煩雑にしないためにIA



へのリンクのみを残します。オブジェクトのインスタンス化を容易にするためだけにUnityが必要であり、オブジェクトの存続期間を制御したくないため、 ExternallyControlledLifetimeManager



使用されます)

コードが記述され、テストが記述され、すべてが環境に優しく、すべてが機能し、本番環境に展開されています。 そして、それが始まりました...



呼び出しコードは次のようになりました。



 private static IT CreateIT() { var a = new A(); Type concreteType = typeof(T); return CreateITInternal(a, concreteType); } static void Main(string[] args) { for (int i = 0; i < 1000; ++i) { CreateIT(); } }
      
      





クラスとインターフェース
 public interface IA { }; public interface IT { }; public class A : IA { } public class T : IT { public T(IA a) { } }
      
      







大丈夫のようです、デバッグとリリースの両方がVisual Studioで正常に動作します。 ただし、.exeファイルを実行すると、この例外により安定してクラッシュします。



 Unhandled Exception: Microsoft.Practices.Unity.ResolutionFailedException: Resolution of the dependency failed, type = "WeakRefTest.IT", name = "(none)". Exception occurred while: while resolving. Exception is: InvalidOperationException - The current type, WeakRefTest.IA, is an interface and cannot be constructed. Are you missing a type mapping? ----------------------------------------------- At the time of the exception, the container was: Resolving WeakRefTest.T,(none) (mapped from WeakRefTest.IT, (none)) Resolving parameter "a" of constructor WeakRefTest.T(WeakRefTest.IA a) Resolving WeakRefTest.IA,(none) ---> System.InvalidOperationException: The current type, WeakRefTest.IA, is an interface and cannot be constructed. Are you missing a type mapping?
      
      





スタックトレース
  at Microsoft.Practices.ObjectBuilder2.DynamicMethodConstructorStrategy.ThrowForAttemptingToConstructInterface(IBuilderContext context) at lambda_method(Closure , IBuilderContext ) at Microsoft.Practices.ObjectBuilder2.DynamicBuildPlanGenerationContext.<>c__DisplayClass1.<GetBuildMethod>b__0(IBuilderContext context) at Microsoft.Practices.ObjectBuilder2.DynamicMethodBuildPlan.BuildUp(IBuilderContext context) at Microsoft.Practices.ObjectBuilder2.BuildPlanStrategy.PreBuildUp(IBuilderContext context) at Microsoft.Practices.ObjectBuilder2.StrategyChain.ExecuteBuildUp(IBuilderContext context) at Microsoft.Practices.ObjectBuilder2.BuilderContext.NewBuildUp(NamedTypeBuildKey newBuildKey) at Microsoft.Practices.Unity.ObjectBuilder.NamedTypeDependencyResolverPolicy.Resolve(IBuilderContext context) at lambda_method(Closure , IBuilderContext ) at Microsoft.Practices.ObjectBuilder2.DynamicBuildPlanGenerationContext.<>c__DisplayClass1.<GetBuildMethod>b__0(IBuilderContext context) at Microsoft.Practices.ObjectBuilder2.DynamicMethodBuildPlan.BuildUp(IBuilderContext context) at Microsoft.Practices.ObjectBuilder2.BuildPlanStrategy.PreBuildUp(IBuilderContext context) at Microsoft.Practices.ObjectBuilder2.StrategyChain.ExecuteBuildUp(IBuilderContext context) at Microsoft.Practices.Unity.UnityContainer.DoBuildUp(Type t, Object existing, String name, IEnumerable`1 resolverOverrides) --- End of inner exception stack trace --- at Microsoft.Practices.Unity.UnityContainer.DoBuildUp(Type t, Object existing, String name, IEnumerable`1 resolverOverrides) at Microsoft.Practices.Unity.UnityContainer.Resolve(Type t, String name, ResolverOverride[] resolverOverrides) at Microsoft.Practices.Unity.UnityContainerExtensions.Resolve[T](IUnityContainer container, ResolverOverride[] overrides) at WeakRefTest.Program.CreateITInternal(IA a, Type targetType) at WeakRefTest.Program.CreateIT() at WeakRefTest.Program.Main(String[] args)
      
      









ワット? オブジェクトを2行上のコンテナーに追加しました...



すべての種類のログは、すべてが機能し、どのように機能するかを示しました。 オブジェクトは実際にコンテナに登録されており、すべてが最初の反復からは程遠いものです。



ドキュメントを少し読み直したところ、容疑者は次のようになりました: ExternallyControlledLifetimeManager



、その中にコンテナに配置されたオブジェクトへの弱い参照が格納されています例外。 しかし、一方で、タイプIA



オブジェクトは、コンテナからの弱いリンクが唯一の場合にのみ削除できますが、この場合はそうではありません! CreateITInternal



CreateIT



両方には、スタック上でこのオブジェクトへの強力なリンクがあり、少なくともCreateIT



終了するまでその寿命を延ばす必要があります。 かどうか? 私たちはチェックします:



  private static IT CreateITInternal(IA a, Type targetType) { using (UnityContainer cont = new UnityContainer()) { cont.RegisterInstance<IA>(a, new ExternallyControlledLifetimeManager()); cont.RegisterType(typeof(IT), targetType, new ExternallyControlledLifetimeManager()); GC.Collect(); return cont.Resolve<IT>(); } }
      
      







現在、最初の反復でクラッシュします。 つまり それは本当にガーベッジコレクションと弱いリンクについてです。



より簡単な例:



  private static void TestWeakRef() { var sa = new A(); var wa = new WeakReference<A>(sa); GC.Collect(); A sa2; wa.TryGetTarget(out sa2); Console.WriteLine("{0}", sa2 == null ? "null" : "not null"); Console.ReadLine(); }
      
      







Visual Studioでは、スタジオの外で実行すると「not null」が表示され、「null」が返されます。



どうやら、コード内の強力なリンクの存在は、これらのリンクが範囲外になるまでオブジェクトの寿命を延長せず、これらのリンクの実際の逆参照が重要です。



修正は非常に簡単です:

  private static IT CreateITInternal(IA a, Type targetType) { using (UnityContainer cont = new UnityContainer()) { cont.RegisterInstance<IA>(a, new ExternallyControlledLifetimeManager()); cont.RegisterType(typeof(IT), targetType, new ExternallyControlledLifetimeManager()); IT ret = cont.Resolve<IT>(); GC.KeepAlive(a); return ret; } }
      
      







そのため、弱いリンクを使用して、ガベージコレクタが予想よりも積極的になる可能性があることに注意してください。



しかし、VSで実行しているときに常に機能する理由はまだ不明です。



All Articles