首页 > 解决方案 > AssemblyLoadContext 未正确卸载

问题描述

我对 in 中的方法有奇怪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);
        }
    }
}

标签: c#.net-core

解决方案


文档中:

备注

  • 一个 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}");
}

推荐阅读