AssemblyLoadContext无法正确卸载

时间:2019-04-15 16:10:09

标签: c# .net-core

我对Unload中的方法AssemblyLoadContext感到奇怪。原始代码来自https://github.com/dotnet/coreclr/pull/22221/files/a7cbc5c8d1bd48cafec48ac50900ff9e96c1485c#diff-cf594171be5712641ea4416aadc2f83f,我使用的是dotnet core 3.0.100-preview3-010431

在这种情况下效果很好:

using System;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Loader;

namespace example
{
    class TestAssemblyLoadContext : AssemblyLoadContext
    {
        public TestAssemblyLoadContext() : base(true)
        {
        }

        protected override Assembly Load(AssemblyName name)
        {
            return null;
        }
    }

    class Manager
    {
        [MethodImpl(MethodImplOptions.NoInlining)]
        public void Execute(out WeakReference testAlcWeakRef)
        {
            var alc = new TestAssemblyLoadContext();
            testAlcWeakRef = new WeakReference(alc);
            alc.Resolving += (alc2, assemblyName) =>
            {
                var dllName = assemblyName.Name.Split(',').First();
                return alc2.LoadFromAssemblyPath(@"absolute\path\lib.dll");
            };

            Assembly a = alc.LoadFromAssemblyPath(@"absolute\path\lib.dll");
            var args = new object[] { 3, 2 };

            var methodInfo = a.GetExportedTypes()[0].GetMethods().Where(m => m.Name == "MethodName").ToList()[0];
            var result = methodInfo.Invoke(Activator.CreateInstance(a.GetExportedTypes()[0]), args);
            alc.Unload();
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public void Unload(WeakReference testAlcWeakRef)
        {
            for (int i = 0; testAlcWeakRef.IsAlive && (i < 10); i++)
            {
                GC.Collect();
                GC.WaitForPendingFinalizers();
            }

            Console.WriteLine($"is alive: {testAlcWeakRef.IsAlive}");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var manager = new Manager();
            manager.Execute(out var testAlcWeakRef);
            manager.Unload(testAlcWeakRef);
        }
    }
}

但是我以后需要调用Unload方法。因此,我将alc.Unload()移到了manager.Unload()中,而alc.Unload()无法正常工作。我在做什么错了?

不起作用的情况:

using System;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Loader;

namespace example
{
    class TestAssemblyLoadContext : AssemblyLoadContext
    {
        public TestAssemblyLoadContext() : base(true)
        {
        }

        protected override Assembly Load(AssemblyName name)
        {
            return null;
        }
    }

    class Manager
    {
        [MethodImpl(MethodImplOptions.NoInlining)]
        public void Execute(out WeakReference testAlcWeakRef, out TestAssemblyLoadContext alc)
        {
            alc = new TestAssemblyLoadContext();
            testAlcWeakRef = new WeakReference(alc);
            alc.Resolving += (alc2, assemblyName) =>
            {
                var dllName = assemblyName.Name.Split(',').First();
                return alc2.LoadFromAssemblyPath(@"absolute\path\lib.dll");
            };

            Assembly a = alc.LoadFromAssemblyPath(@"absolute\path\lib.dll");
            var args = new object[] { 3, 2 };

            var methodInfo = a.GetExportedTypes()[0].GetMethods().Where(m => m.Name == "MethodName").ToList()[0];
            var result = methodInfo.Invoke(Activator.CreateInstance(a.GetExportedTypes()[0]), args);
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public void Unload(WeakReference testAlcWeakRef, TestAssemblyLoadContext alc)
        {
            alc.Unload();
            for (int i = 0; testAlcWeakRef.IsAlive && (i < 10); i++)
            {
                GC.Collect();
                GC.WaitForPendingFinalizers();
            }

            Console.WriteLine($"is alive: {testAlcWeakRef.IsAlive}");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var manager = new Manager();
            manager.Execute(out var testAlcWeakRef, out var alc);
            manager.Unload(testAlcWeakRef, alc);
        }
    }
}

1 个答案:

答案 0 :(得分:0)

来自documentation

  

备注

     
      
  • 只有可收集的AssemblyLoadContext才能卸载。
  •   
  • 卸载将异步进行。
  •   
  • 在引用AssemblyLoadContext时不会发生卸载。
  •   

您的 alc 变量阻止了上下文的卸载。

可能的解决方案:

[MethodImpl(MethodImplOptions.NoInlining)]
public void Unload(WeakReference testAlcWeakRef, ref TestAssemblyLoadContext alc)
{
    alc.Unload(); 
    alc = null;

    for (int i = 0; testAlcWeakRef.IsAlive && (i < 10); i++)
    {
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }

    Console.WriteLine($"is alive: {testAlcWeakRef.IsAlive}");
}