This is a netcore question, not a NETFX question.
This workup demonstrates the behaviour. You will need to supply an assembly for it to load and make the name in the code match the name in the file system.
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}")));
}
}
}
}
If you run this you will see that unload events fire but both instances of the assembly continue to be found by currentDomain.GetAssemblies()
.
Documentation for AssemblyLoadContext.Unload()
says
- An AssemblyLoadContext can only be unloaded if it is collectible.
- Unloading will occur asynchronously.
- Unloading will not occur while there are references to the AssemblyLoadContext
I believe this example meets all these requirements. What else might be interfering? Are there any known bugs with this?
Is it possible the assemblies actually unloaded but currentDomain has failed to update its bookkeeping? How would you tell?