You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 6.0/7.0:如何强制回收全局变量以卸载AssemblyLoadContext并删DLL?

动态加载程序集卸载问题

目的

  • 将程序集加载至独立的AssemblyLoadContext
  • 从中创建类型
  • 基于该类型创建实例
  • 反复调用实例的方法
  • 当程序集不再需要时(可能是数天后),卸载AssemblyLoadContext并删除程序集文件

问题

已将程序集、类型及实例的所有引用置空,尝试卸载AssemblyLoadContext失败,无法删除DLL文件。

代码演示1

以下代码为当前实现逻辑,无法删除DLL文件:

namespace MyDll
{
    public class MyText
    {
        public string m_text;

        public MyText(string text)
        {
            m_text = text;
        }

        public string GetText()
        {
            return m_text;
        }
    }

    public class MyClass
    {
        public string GetTextByBytes(byte[] bytes)
        {
            string json = Encoding.UTF8.GetString(bytes);
            var obj = JsonConvert.DeserializeObject<MyText>(json);
            return obj.GetText();
        }

        public string GetTextByObject(MyText text)
        {
            return text.GetText();
        }

        public string GetGuidText(Guid guid)
        {
            return guid.ToString();
        }

        public string GetText(string str)
        {
            return str;
        }

        public List<string> GetMultipleTexts(List<string> lst)
        {
            return lst;
        }

    }
}

static void Main(string[] args)
{
    var ctx = new AssemblyLoadContext(null, isCollectible: true);

    var assembly = ctx.LoadFromAssemblyPath(dllFilePath);
    var myType = assembly.GetType("MyDll.MyClass");
    var instance = Activator.CreateInstance(myType, new object[] { }, null);
    var myMethod = myType.GetMethod("GetGuidText");
    var res = myMethod.Invoke(instance, new object[] { Guid.NewGuid() });

    assembly = null;
    myType = null;
    instance = null;
    myMethod = null;
    GC.Collect();
    GC.WaitForPendingFinalizers();
    Thread.Sleep(2000);
    GC.Collect();
    GC.WaitForPendingFinalizers();

    ctx.Unload();

    GC.Collect();
    GC.WaitForPendingFinalizers();
    Thread.Sleep(2000);
    GC.Collect();
    GC.WaitForPendingFinalizers();

    File.Delete(dllFilePath);          
    Console.WriteLine($"Done!");
}

代码演示2

将代码放入独立函数Test()后,即可成功删除DLL:

private static void Test(AssemblyLoadContext ctx)
{
    var assembly = ctx.LoadFromAssemblyPath(dllFilePath);
    var myType = assembly.GetType("MyDll.MyClass");
    var instance = Activator.CreateInstance(myType, new object[] { }, null);
    var myMethod = myType.GetMethod("GetGuidText");
    var res = myMethod.Invoke(instance, new object[] { Guid.NewGuid() });
}

static void Main(string[] args)
{
    var ctx = new AssemblyLoadContext(null, isCollectible: true);
    Test(ctx);
    ctx.Unload();

    GC.Collect();
    GC.WaitForPendingFinalizers();
    Thread.Sleep(2000);
    GC.Collect();
    GC.WaitForPendingFinalizers();

    File.Delete(dllFilePath);         
    Console.WriteLine($"Done!");
}

无法改为演示2的原因

在SkyBridge应用中,加载的程序集、类型及实例需存入全局集合,供独立线程调用,无法像演示2那样放入函数局部变量让其超出作用域。只能在程序集无用时从全局集合移除、置空引用,但GC无法回收,导致卸载失败。

最终问题

在不将类型和实例改为函数局部变量的情况下,如何修改演示1的代码以实现AssemblyLoadContext卸载及DLL删除?


2023-06-02补充
目前问题已更清晰:函数内的局部变量在函数结束后超出作用域,调用GC.Collect()可立即被回收;但全局变量即便置空后调用GC.Collect(),也无法被回收。

是否有办法让已置空的全局变量在GC.Collect()后立即被回收?


内容的提问来源于stack exchange,提问作者user788454

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 00:07:10