【发布时间】:2021-05-11 16:00:21
【问题描述】:
这是一个 netcore 问题,而不是 NETFX 问题。
此检查演示了该行为。您需要提供一个程序集以供其加载并使代码中的名称与文件系统中的名称匹配。
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Threading;
namespace unload
{
public class ALC : AssemblyLoadContext
{
public ALC() : base(isCollectible: true) { }
protected override Assembly Load(AssemblyName name) => null;
}
class Program
{
static void Main(string[] args)
{
var alc1 = new ALC();
var alc2 = new ALC();
Assembly assy1, assy2;
using (var stream = new FileStream("./assy.dll", FileMode.Open))
assy1 = alc1.LoadFromStream(stream);
using (var stream = new FileStream("./assy.dll", FileMode.Open))
assy2 = alc2.LoadFromStream(stream);
var currentDomain = AppDomain.CurrentDomain;
alc1.Unloading += alc =>
{
Console.WriteLine("alc1 HAS UNLOADED");
};
alc2.Unloading += alc =>
{
Console.WriteLine("alc2 HAS UNLOADED");
};
alc1.Unload();
assy1 = null;
alc1 = null;
alc2.Unload();
assy2 = null;
alc2 = null;
GC.Collect(); //no refs and force GC
Thread.Sleep(100); // let other things complete
var duplicates = from a in currentDomain.GetAssemblies() group a by a.FullName into g where g.Count() > 1 select g;
while (duplicates.Any())
{
GC.Collect();
Thread.Sleep(500);
duplicates = from a in currentDomain.GetAssemblies() group a by a.FullName into g where g.Count() > 1 select g;
Console.WriteLine(string.Join(", ", duplicates.Select(g => $"{g.Count()} {g.Key}")));
}
}
}
}
如果您运行此程序,您将看到卸载事件触发,但 currentDomain.GetAssemblies() 继续找到程序集的两个实例。
AssemblyLoadContext.Unload() 的文档说
- 只有可收集的 AssemblyLoadContext 才能被卸载。
- 卸载将异步进行。
- 存在对 AssemblyLoadContext 的引用时不会发生卸载
我相信这个例子符合所有这些要求。还有什么可能干扰?这有什么已知的错误吗?
是否有可能实际卸载了程序集但 currentDomain 未能更新其簿记?你会怎么看?
【问题讨论】:
-
这是一个
Unloading事件,而不是Unloaded,它可能还没有完全卸载。在您正在卸载的函数之外尝试GC.Collect(2); GC.WaitForPendingFinalizers(); GC.Collect(2);。 -
@Charlieface 看到我的自我回答。打扰发布版本的有力论据!性能开销从来没有让我担心,但内存泄漏是另一回事。
-
见stackoverflow.com/questions/755680/… 这就是为什么我说在函数之外
标签: c# .net-core assembly-loading