assembly.loadfile无法卸载,因其直接加载到不可卸载的default assemblyloadcontext中,导致文件锁定、类型冲突且无unload接口。

Assembly.LoadFile 不能用于热更新——它加载的程序集会永久驻留在默认 AssemblyLoadContext 中,无法卸载,且会引发类型冲突和文件锁定。
为什么 Assembly.LoadFile 加载后根本卸载不掉?
很多人试过用 Assembly.LoadFile("plugin.dll") 加载、调用、再设为 null、强制 GC.Collect(),结果发现:DLL 文件依然被锁住,下次加载报 IOException;旧类型还在内存里,新版本同名类反射失败;堆栈里出现 DynamicClass 或路径乱码。这是因为:
-
LoadFile绕过 CLR 的程序集解析逻辑,直接从磁盘读取字节并加载进AssemblyLoadContext.Default -
AssemblyLoadContext.Default是不可卸载的——无论你做什么,它都一直存在 - 它不会自动处理依赖,若插件引用了
Newtonsoft.Json,而主程序也用了,就可能触发FileLoadException: 混合版本冲突
LoadFile 和 LoadFrom 的关键区别在哪?
两者都按路径加载,但行为截然不同:
-
LoadFile:完全隔离路径,即使两个 DLL 文件名相同、内容不同、路径不同,也会被当作“不同程序集”加载两次——导致同一类型出现两个副本,跨 DLL 调用时直接抛InvalidCastException -
LoadFrom:有“同名合并”机制,相同简单名称(不含版本/签名)的程序集,第二次调用会返回已加载的那个——看似省事,实则掩盖了版本漂移问题;而且仍走Default上下文,照样无法卸载 - 两者都不支持
Unload(),也不接受isCollectible = true参数,本质是“一次性加载,永久驻留”
想真正热更,必须绕开 LoadFile 和 LoadFrom
可行路径只有一条:AssemblyLoadContext(ALC)配合 LoadFromAssemblyPath。但要注意几个硬性前提:
- 必须新建 collectible ALC:
var alc = new AssemblyLoadContext(isCollectible: true) - 加载只能用
alc.LoadFromAssemblyPath("path.dll"),不能用Assembly.LoadFile或静态LoadFrom - 所有反射操作(
GetType、Activator.CreateInstance、GetMethod)必须在该 ALC 内完成,否则类型不互通 - 卸载前必须确保:无任何对该 ALC 内类型的强引用、无后台线程、无未注销事件、无
Timer或FileStream残留——漏一项就Unload()失败或静默卡死
Unity 项目里别碰 LoadFile,尤其别用在 iOS/Android
IL2CPP 构建下,LoadFile 会直接崩溃或静默失败;HybridCLR 要求热更 DLL 必须由 Unity 同版本 il2cpp 工具链生成,LoadFile 加载的二进制不满足元数据兼容性,运行时报 MissingMethodException。ILRuntime 虽不依赖 ALC,但它自己封装了 AppDomain 隔离层,底层仍是模拟执行 IL,不是靠 LoadFile 加载原生程序集——强行混用只会让调试器失去符号、断点失效、堆栈不可读。










