C#报”CannotUnloadAppDomainException”的原因以及解决办法

  • Post category:C#
  1. 原因分析

在 .Net 程序运行时,每个应用程序域(AppDomain)都有其自身的根程序集(Root Assembly)和资源。应用程序域有自己的加载器来加载需要的程序集和资源。当使用 System.AppDomain.Unload() 方法或 AppDomain.CurrentDomain.DomainUnload 事件来卸载域时,可能会抛出 CannotUnloadAppDomainException 异常。这是因为应用程序域中可能存在一些资源或程序集无法被释放导致。

  1. 解决办法

为了解决这个问题,我们需要找到程序域中不能被卸载的资源,同时对它们进行释放。

2.1 示例一:释放资源

首先,我们需要进行一些排查工作,查找可能导致 CannotUnloadAppDomainException 异常的原因。可以尝试使用以下方式排查问题:

AppDomain.CurrentDomain.DomainUnload += (sender, e) =>
{
   Console.WriteLine("Domain Unload started");

   foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
   {
       try
       {
           Console.WriteLine($"Trying to unload {assembly.FullName}");

           if (assembly.IsDynamic) continue;

           AppDomain.CurrentDomain.ReflectionOnlyLoad(assembly.FullName);
       }
       catch (Exception ex)
       {
           Console.WriteLine($"Cannot unload assembly {assembly.FullName}, exception: {ex}");
       }
   }
};

以上代码可以输出当前程序域中的所有程序集,并在卸载过程中进行释放。如果在释放中出现异常,则会输出异常信息。

2.2 示例二:动态加载程序集

其次,我们可以将一些需要动态加载的程序集放在应用程序域之外,以方便卸载时的释放。可以使用 Assembly.LoadFrom() 方法来动态加载需要的程序集。

private static void LoadAssembly(string path)
{
    var assembly = Assembly.LoadFrom(path);
    Console.WriteLine($"Loaded assembly {assembly.FullName} from {path}");
}

以上代码会将需要动态加载的程序集放在应用程序域之外,卸载时可以更轻松地释放。