.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
相关产品推荐
相关产品推荐

