AssemblyLoadContext 可卸载程序集但需满足严格条件:构造时设 isCollectible = true,避免强引用、静态字段、线程或 Finalizer;.NET Core 3.0+ 支持,Framework 不支持。

AssemblyLoadContext 能否真正卸载程序集
不能直接卸载,但可以卸载整个 AssemblyLoadContext 实例及其加载的所有程序集——前提是该上下文是“可卸载的”(isCollectible = true),且其中没有强引用、静态字段、线程或 Finalizer 持有对象。.NET Core 3.0+ 支持此机制,.NET Framework 不支持。
常见错误现象:AssemblyLoadContext.Unload() 返回后,内存未释放、类型仍可访问、GC 不回收上下文对象。
- 必须在构造时传入
true:newAssemblyLoadContext(true) - 不能在默认上下文(
AssemblyLoadContext.Default)中加载插件——它不可卸载 - 插件内不能调用
typeof(...).Assembly并跨上下文缓存类型,否则会隐式持有引用 - 插件导出的接口类型必须定义在主程序集(或共享的、由默认上下文加载的程序集)中
如何安全加载和调用插件类型
核心原则:所有跨上下文交互必须通过接口(而非具体类型),且接口定义必须由主程序集提供。插件只负责实现,不参与类型定义。
使用场景:动态加载 DLL 插件,执行某方法后卸载,避免程序集锁定和内存泄漏。
- 主程序定义公共接口,如
IPlugin和void Execute() - 插件项目引用主程序集(仅用于编译,运行时不复制)
- 用自定义
AssemblyLoadContext加载插件 DLL:var context = new AssemblyLoadContext(pluginPath, isCollectible: true); var assembly = context.LoadFromAssemblyPath(pluginPath); var pluginType = assembly.GetType("MyPlugin"); var plugin = Activator.CreateInstance(pluginType) as IPlugin; - 调用完成后,显式调用
context.Unload(),然后等待 GC 回收(可手动GC.Collect(); GC.WaitForPendingFinalizers();辅助验证)
为什么插件里 new Thread 或 Timer 会导致卸载失败
因为线程、定时器回调、事件订阅、静态 Action/Func 等都会在后台维持对插件上下文中类型的强引用,使 AssemblyLoadContext 无法被 GC 回收。
典型错误代码:
public class MyPlugin : IPlugin {
private Timer _timer;
public void Execute() {
_timer = new Timer(_ => { Console.WriteLine("tick"); }, null, 0, 1000);
}
}
- Timer 回调委托捕获了插件实例,而该实例类型来自插件上下文,导致上下文无法卸载
- 解决方案:在
Execute()前或Unload()前主动_timer?.Dispose() - 更稳妥做法:插件实现
IDisposable,主程序在Unload()前先调用plugin.Dispose() - 避免在插件中启动长期存活线程;如需后台任务,改用主程序托管的
Task+ 取消令牌
调试卸载失败的常见手段
当 context.Unload() 后上下文仍存活,说明仍有根引用未清理。这不是黑盒问题,有明确排查路径。
- 用 Visual Studio 的“调试 → Windows → .NET Object Allocation Tracking”观察上下文对象是否被回收
- 用 dotMemory 或 dotTrace 快照比对:卸载前后搜索
AssemblyLoadContext实例数量 - 检查插件中是否用了
AppDomain.CurrentDomain.AssemblyResolve或自定义AssemblyLoadContext.Default.Load回调——这些委托可能跨上下文泄露 - 确保插件 DLL 没被其他地方(如
Assembly.GetExecutingAssembly()、日志框架反射扫描)意外加载进默认上下文
最常被忽略的一点:插件中的 static readonly Dictionary 缓存,只要键值是插件上下文里的 Type,就会阻止整个上下文回收。










