更新:我设法将其导入WinDbg,并查看发生了什么。我发现了两件事:首先,此列表未正确发布:
private Stack<DependencyAssemblyLoadContext> _loadedContexts;
所以我取消了它,绕过我的GC
调用(我知道不好的作法,但是,嘿,这只是一个POC /演示。我担心以后会做得更好。),其中之一我的两个示例DLL已卸载。 (我也将一些列表变成了堆栈,我可以尝试使它们尽快消失。同样,跳过循环,以便将概念演示给人们。)
不过,我注意到的另一件事是,一个示例任务(引发异常)仍然存在。 WinDbg产生以下内容:
0:009> !gcroot -all 0000025e8fc0ffa8
Thread 5824:
000000F0F237E5F0 00007FFDC9361EAE CleanupService.NetCore.Program.Run() [C:\stuff\Program.cs @ 69]
rbp-b0: 000000f0f237e630
-> 0000025E8FC12CB0 System.NotImplementedException
-> 0000025E8FC16230 System.Object[]
-> 0000025E8FC0FFA8 System.Reflection.LoaderAllocator
000000F0F237E5F0 00007FFDC9361EAE CleanupService.NetCore.Program.Run() [C:\stuff\Program.cs @ 69]
rbp-88: 000000f0f237e658
-> 0000025E8FC111C8 System.RuntimeType
-> 0000025E8FC0FFA8 System.Reflection.LoaderAllocator
000000F0F237E5F0 00007FFDC9361EAE CleanupService.NetCore.Program.Run() [C:\stuff\Program.cs @ 69]
rbp-78: 000000f0f237e668
-> 0000025E8FC11268 CleanupService.Sample.SimpleFailedTask
-> 0000025E8FC0FFA8 System.Reflection.LoaderAllocator
如果我没看错,那么异常本身就留在内存中了吗?并且由于它来自试图卸载的程序集中的类,因此程序集不会卸载。
这似乎...很奇怪。如何摆脱异常引用?
此问题的原始说明如下,但是我更新了下面的代码。
我有一个系统的POC,该系统可以扫描一堆DLL,从中加载任务,然后卸载它们。这是.NET Core 3.0,因此我正在使用AssemblyLoadContext
。我一切正常,并且Unloading
事件触发,但是当我看着“模块”窗口时,DLL仍然保持加载状态。这与基于AppDomain
的CLR版本形成鲜明对比,后者在域被删除后即从“模块”窗口中卸载DLL。
我认为我在某处引用了某些内容,但由于公司对管理员权限的限制,我无法弄清哪里,而WinDbg还是很不稳定。
主要应用:
private IFileSystemService _fileSystemService;
private ITaskManagementService _taskManagementService;
static void Main(string[] args)
{
new Program().Run();
}
public Program()
{
_fileSystemService = new FileSystemService();
_taskManagementService = new TaskManagementService();
}
[MethodImpl(MethodImplOptions.NoInlining)]
public void Run()
{
Console.WriteLine("Getting available files...");
var files = _fileSystemService.GetFilePaths("*.dll");
Console.WriteLine($"Found {files.Length} files to scan.");
Console.WriteLine("Scanning for tasks...");
var tasks = _taskManagementService.GetavailablecleanupTasks(files);
Console.WriteLine($"Total tasks found: { tasks.Count() }");
while (tasks.Count > 0)
{
var task = tasks.Pop();
Console.WriteLine($"Executing task: { task.GetType().Name }");
Console.WriteLine("Output:");
Console.ForegroundColor = ConsoleColor.Green;
try
{
task.Execute<object>();
}
catch (Exception ex)
{
ex = null;
continue;
}
finally
{
Console.ForegroundColor = ConsoleColor.Gray;
task = null;
}
}
Console.WriteLine("Cleaning up... ");
_taskManagementService.UnloadAssemblies();
_taskManagementService = null;
Console.WriteLine("Cleanup done.");
GC.WaitForPendingFinalizers();
GC.Collect();
Console.WriteLine("Process complete. Press any key to continue.");
Console.ReadKey();
}
这是TaskManagementService
,用于加载和卸载程序集:
private Stack<DependencyAssemblyLoadContext> _loadedContexts;
public TaskManagementService()
{
_loadedContexts = new Stack<DependencyAssemblyLoadContext>();
}
public Stack<ICleanupTask> GetavailablecleanupTasks(string[] assemblyPaths)
{
// This application is designed to run every 24 hours,so just unload what we have before reloading
UnloadAssemblies();
var results = new Stack<ICleanupTask>();
// Loads the assembly containing the task loader so it can be used. The assembly also contains ICleanupTask and CleanupTask.
foreach (var path in assemblyPaths)
{
var context = new DependencyAssemblyLoadContext(path);
var assemblyName = new AssemblyName(Path.GetFileNameWithoutExtension(path));
var assembly = context.LoadFromAssemblyName(assemblyName);
foreach (var task in GetTasksFromAssembly(assembly))
{
results.Push(task);
}
_loadedContexts.Push(context);
}
return results;
}
public void UnloadAssemblies()
{
if (_loadedContexts.Count == 0)
{
return;
}
while (_loadedContexts.Count > 0)
{
var context = _loadedContexts.Pop();
// context.Unloading += (e) => Console.WriteLine($"Assembly {e.Assemblies.First().GetName()} unloading...");
context.Unload();
context = null;
}
}
private List<ICleanupTask> GetTasksFromAssembly(Assembly assembly)
{
var tasks = new List<ICleanupTask>();
foreach (var type in assembly.GetTypes())
{
if (type.IsSubclassOf(typeof(CleanupTask)))
{
tasks.Add(Createtask(type));
}
}
return tasks;
}
private ICleanupTask Createtask(Type taskType)
{
return (ICleanupTask)activator.CreateInstance(taskType);
}