Pav*_*l Z 5 c# json.net .net-core-3.0
我们需要为我们的软件卸载插件。插件被加载到单独的 AssemblyLoadContext(.net core 3.0)中,并通过序列化数据与主软件进行通信。如果使用 Json.NET 进行序列化,则永远不会释放对已序列化对象类型的引用,如果在插件中定义了该类型,则永远不会卸载 AssemblyLoadContext。
我们设法使用复杂的反射魔法实现了插件卸载 - System.ComponentModel.TypeConverter 程序集中的一些手动缓存清理,但它看起来像一个肮脏的黑客。
另一种选择是在插件中包含 Newtonsoft.Json 并在单独的上下文中加载它。尽管即使不仅 Newtonsoft.Json.dll 甚至 System.ComponentModel.TypeConverter.dll 都在单独的上下文中加载,它也无济于事。缓存仍然填充在默认上下文中。
有谁知道,在使用 Json.NET 对其类型进行序列化后,是否有一种卸载插件的好方法?
提前谢谢了!
卸载插件的 AssemblyLoadContext 后,您是否正确清除了弱引用?
看看这个文档:
https://learn.microsoft.com/en-us/dotnet/standard/ assembly/unloadability
但是,卸载不会立即完成。如前所述,它依赖垃圾收集器从测试程序集中收集所有对象。在许多情况下,无需等待卸载完成。但是,在某些情况下,了解卸载已完成是很有用的。例如,您可能想要删除从磁盘加载到自定义 AssemblyLoadContext 中的程序集文件。在这种情况下,可以使用以下代码片段。它触发垃圾收集并在循环中等待挂起的终结器,直到对自定义 AssemblyLoadContext 的弱引用设置为 null,表示目标对象已被收集。在大多数情况下,只需要通过一次循环。但是,对于由在 AssemblyLoadContext 中运行的代码创建的对象具有终结器的更复杂的情况,可能需要更多遍。
[MethodImpl(MethodImplOptions.NoInlining)]
static int ExecuteAndUnload(string assemblyPath, out WeakReference alcWeakRef)
{
var alc = new TestAssemblyLoadContext();
Assembly a = alc.LoadFromAssemblyPath(assemblyPath);
alcWeakRef = new WeakReference(alc, trackResurrection: true);
var args = new object[1] {new string[] {"Hello"}};
int result = (int) a.EntryPoint.Invoke(null, args);
alc.Unload();
return result;
}
Run Code Online (Sandbox Code Playgroud)
执行你的逻辑,卸载并获取弱引用:
WeakReference testAlcWeakRef;
int result = ExecuteAndUnload("absolute/path/to/your/assembly", out testAlcWeakRef);
Run Code Online (Sandbox Code Playgroud)
运行垃圾收集并等待弱引用的终结器处于活动状态。如果需要,可以重复最多 10 次:
for (int i = 0; testAlcWeakRef.IsAlive && (i < 10); i++)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
Run Code Online (Sandbox Code Playgroud)
也看看这个线程,它看起来与您所描述的非常相似: https: //github.com/dotnet/runtime/issues/13283