I have an application that loads external assemblies which I have no control over (similar to a plugin model where other people create and develop assemblies that are used by the main application). It loads them by creating new AppDomains for these assemblies and then when the assemblies are done being used, the main AppDomain unloads them.
Currently, it simplistically unloads these assemblies by
try { AppDomain.Unload(otherAssemblyDomain); } catch(Exception exception) { // log exception }
However, on occasion, exceptions are thrown during the unloading process specifically CannotUnloadAppDomainException
. From what I understand, this can be expected since a thread in the children AppDomains cannot be forcibly aborted due to situations where unmanaged code is still being executed or the thread is in a finally
block:
When a thread calls Unload, the target domain is marked for unloading. The dedicated thread attempts to unload the domain, and all threads in the domain are aborted. If a thread does not abort, for example because it is executing unmanaged code, or because it is executing a finally block, then after a period of time a CannotUnloadAppDomainException is thrown in the thread that originally called Unload. If the thread that could not be aborted eventually ends, the target domain is not unloaded. Thus, in the .NET Framework version 2.0 domain is not guaranteed to unload, because it might not be possible to terminate executing threads.
My concern is that if the assembly is not loaded, then it could cause a memory leak. A potential solution would be to kill the main application process itself if the above exception occurs but I rather avoid this drastic action.
I was also considering repeating the unloading call for a few additional attempts. Perhaps a constrained loop like this:
try { AppDomain.Unload(otherAssemblyDomain); } catch (CannotUnloadAppDomainException exception) { // log exception var i = 0; while (i < 3) // quit after three tries { Thread.Sleep(3000); // wait a few secs before trying again... try { AppDomain.Unload(otherAssemblyDomain); } catch (Exception) { // log exception i++; continue; } break; } }
Does this make sense? Should I even bother with trying to unload again? Should I just try it once and move on? Is there something else I should do? Also, is there anything that can be done from the main AppDomain to control the external assembly if threads are still running (keep in mind others are writing and running this external code)?
I'm trying understand what are best practices when managing multiple AppDomains.
You can not unload an assembly from an appdomain. You can destroy appdomains, but once an assembly is loaded into an appdomain, it's there for the life of the appdomain.
To unload an assembly in the . NET Framework, you must unload all of the application domains that contain it. To unload an application domain, use the AppDomain. Unload method.
The AppDomain class implements a set of events that enable applications to respond when an assembly is loaded, when an application domain will be unloaded, or when an unhandled exception is thrown. Advantages. A single CLR operating system process can contain multiple application domains.
The CurrentDomain property is used to obtain an AppDomain object that represents the current application domain. The FriendlyName property provides the name of the current application domain, which is then displayed at the command line.
I've dealt with a similar problem in my app. Basically, you can't do anything more to force the AppDomain
to go down than Unload
does.
It basically calls abort of all threads that are executing code in the AppDomain
, and if that code is stuck in a finalizer or unmanaged code, there isn't much that can be done.
If, based on the program in question, it's likely that the finalizer/unmanaged code will finish some later time, you can absolutely call Unload
again. If not, you can either leak the domain on purpose or cycle the process.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With