聊一聊 .NET 的 AssemblyLoadContext 可插拔程序集
聊一聊 .NET 的 AssemblyLoadContext 可插拔程序集引言程序集加载的痛点在传统的 .NET Framework 时代应用程序域AppDomain提供了程序集隔离的能力但它的使用较为复杂且性能开销较大。到了 .NET Core/.NET 5微软引入了AssemblyLoadContextALC这是一种轻量级的程序集加载上下文机制。ALC 的核心价值在于允许开发者在同一个进程中加载多个版本的程序集、实现动态卸载、构建插件系统等。本文将深入剖析 ALC 的原理并通过代码示例展示其可插拔能力。## ALC 的核心原理### 什么是 AssemblyLoadContextAssemblyLoadContext是 .NET 中用于管理程序集加载和隔离的抽象类。每个 ALC 实例维护一个独立的程序集缓存、类型解析逻辑和依赖关系图。默认情况下.NET 运行时使用AssemblyLoadContext.Default默认上下文加载核心库和应用程序主程序集。通过自定义 ALC我们可以-隔离依赖不同插件可以使用不同版本的程序集而不会冲突。-动态卸载通过Unload()方法释放不再需要的程序集和类型仅适用于可收集的 ALC。-可插拔加载从非标准路径如网络、加密文件、内存流加载程序集。### 工作原理图解ALC 的加载流程如下1. 程序集请求进入 ALC。2. ALC 检查自身缓存是否已有该程序集。3. 若未命中调用Load或LoadFromAssemblyName方法可重写。4. 解析依赖时递归调用父上下文通常是默认上下文。5. 加载完成后程序集被缓存到该 ALC 中。这种设计使得每个 ALC 可以拥有独立的程序集视图实现“沙箱”效果。## 可插拔场景构建插件系统假设我们需要一个日志插件系统主程序加载不同版本的日志库而插件之间互不干扰。下面通过两个代码示例演示如何实现。### 示例 1基本 ALC 实现——从指定路径加载程序集csharpusing System;using System.Reflection;using System.Runtime.Loader;// 自定义 ALC从插件目录加载程序集public class PluginLoadContext : AssemblyLoadContext{ private readonly string _pluginPath; public PluginLoadContext(string pluginPath) : base(isCollectible: true) // 启用可收集支持卸载 { _pluginPath pluginPath; } // 重写 Load 方法指定程序集加载路径 protected override Assembly Load(AssemblyName assemblyName) { // 检查是否是插件程序集 if (assemblyName.Name MyPlugin) { string assemblyFile System.IO.Path.Combine(_pluginPath, MyPlugin.dll); return LoadFromAssemblyPath(assemblyFile); } // 其他程序集如 System.Runtime回退到默认上下文 return AssemblyLoadContext.Default.LoadFromAssemblyName(assemblyName); }}class Program{ static void Main() { // 创建两个不同的 ALC 实例模拟插件隔离 var context1 new PluginLoadContext(C:\Plugins\Version1); var context2 new PluginLoadContext(C:\Plugins\Version2); // 加载插件假设 MyPlugin 中有 IPlugin 接口实现 var plugin1 context1.LoadFromAssemblyName(new AssemblyName(MyPlugin)); var plugin2 context2.LoadFromAssemblyName(new AssemblyName(MyPlugin)); Console.WriteLine($插件1程序集引用{plugin1.FullName}); Console.WriteLine($插件2程序集引用{plugin2.FullName}); // 验证隔离性两个 ALC 中的 MyPlugin 是不同对象 Console.WriteLine($程序集是否相同{plugin1 plugin2}); // 输出 False }}注释说明-isCollectible: true启用卸载能力但需要确保无外部引用。- 重写Load方法时优先加载插件程序集非插件程序集如 BCL回退到默认上下文避免重复加载。- 通过两个 ALC 实例加载相同名称的程序集它们的内存副本和类型是独立的。### 示例 2动态卸载与释放资源csharpusing System;using System.Reflection;using System.Runtime.Loader;// 可卸载的插件上下文public class UnloadablePluginContext : AssemblyLoadContext{ public UnloadablePluginContext() : base(isCollectible: true) { } protected override Assembly Load(AssemblyName assemblyName) { // 假设插件文件为 Plugin.dll if (assemblyName.Name Plugin) { string pluginDir System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Plugins); string pluginPath System.IO.Path.Combine(pluginDir, Plugin.dll); return LoadFromAssemblyPath(pluginPath); } return AssemblyLoadContext.Default.LoadFromAssemblyName(assemblyName); }}class Program{ static void Main() { WeakReference alcWeakRef; WeakReference assemblyWeakRef; // 创建作用域确保引用被释放 CreateAndLoadPlugin(out alcWeakRef, out assemblyWeakRef); // 强制垃圾回收 GC.Collect(); GC.WaitForPendingFinalizers(); Console.WriteLine($ALC 是否存活{alcWeakRef.IsAlive}); // 输出 False Console.WriteLine($程序集是否存活{assemblyWeakRef.IsAlive}); // 输出 False } static void CreateAndLoadPlugin(out WeakReference alcWeakRef, out WeakReference assemblyWeakRef) { var context new UnloadablePluginContext(); var assembly context.LoadFromAssemblyName(new AssemblyName(Plugin)); alcWeakRef new WeakReference(context); assemblyWeakRef new WeakReference(assembly); // 卸载上下文 context.Unload(); Console.WriteLine(ALC 已卸载); }}注释说明- 使用WeakReference跟踪 ALC 和程序集的生命周期。- 调用context.Unload()标记 ALC 为可卸载状态但实际释放发生在 GC 回收时。- 在作用域结束后强制 GC 回收验证 ALC 和程序集被彻底释放。这避免了内存泄漏适用于长期运行的插件系统。## 高级议题冲突解决与共享类型### 类型加载冲突当主程序和插件通过不同 ALC 加载相同类型的程序集时CLR 会将其视为不同的类型。例如主程序中的ILogger接口和插件中的ILogger接口即使代码相同不兼容。解决方案包括-共享接口程序集将公共接口放在一个独立的程序集中由默认上下文加载。-使用弱引用或动态调用通过dynamic或反射避免强类型绑定。### 依赖解析链ALC 的Load方法可以实现自定义依赖解析策略。例如从 NuGet 缓存或远程服务器加载程序集csharpprotected override Assembly Load(AssemblyName assemblyName){ // 尝试从自定义缓存加载 string cachePath GetCachePath(assemblyName); if (File.Exists(cachePath)) return LoadFromAssemblyPath(cachePath); // 回退到默认 return AssemblyLoadContext.Default.LoadFromAssemblyName(assemblyName);}## 总结AssemblyLoadContext是 .NET 中实现程序集隔离和可插拔设计的核心工具。通过自定义 ALC我们可以- 在进程内加载多个版本的程序集避免“DLL地狱”。- 构建插件系统支持动态安装、卸载和升级。- 控制程序集加载源增强安全性和灵活性。然而使用 ALC 也需注意类型隔离可能带来兼容性问题卸载需要谨慎处理无引用性能开销略高于默认加载。在实际项目中应结合场景权衡使用。掌握 ALC 原理能让你的 .NET 应用具备更强的模块化能力。

相关新闻

LangChain → AgentExecutor → LangGraph 的演进脉络

LangChain → AgentExecutor → LangGraph 的演进脉络

LangGraph 构建于 LangChain 之上,能极大简化智能体(Agent)与智能体运行时(Agent Runtime)的开发工作。 那么智能体和智能体运行时究竟指什么? 在 LangChain 中,我们对智能体的定义是&#xff1…

2026/10/31 13:45:21 阅读更多 →
AI 加速器(TPU/NPU/GPGPU)Linux 驱动栈技术分析

AI 加速器(TPU/NPU/GPGPU)Linux 驱动栈技术分析

本文浅析了谷歌 TPU 与国内 AI 加速器厂商在 Linux 上的内核驱动与内存(HBM)管理机制,以及它们为何普遍不采用 DRM 框架的原因。 目录 1. 背景:AI 加速器的物理内存2. 谷歌 TPU 的驱动与 HBM 管理3. 国内 AI 加速器厂商驱动栈全景…

2026/11/3 18:43:27 阅读更多 →
Transformer 与 LLM 本质深度拆解

Transformer 与 LLM 本质深度拆解

Transformer 与 LLM 本质深度拆解先一句话总纲: Transformer 是一种神经网络架构;LLM(大语言模型)是一类任务目标。现代 LLM 几乎全部基于 Transformer Decoder 构建。一、Transformer 的本质论文:Attention Is All Yo…

2026/11/1 20:01:38 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/11/3 1:09:25 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/11/3 10:57:27 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/11/3 11:35:49 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/11/3 2:27:21 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/11/3 10:47:27 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/11/3 10:57:33 阅读更多 →

日新闻

周新闻

月新闻