I am hosting a WCF service where the requirements are that an object, of a type the WCF service is not directly referenced, is invoked and some (common) methods run on it. The type is created via reflection and AssemblyResolve: this is OK.
I then got to thinking -- we are expecting maybe 50 - 100 of these assemblies / types to arrive, especially when we are versioning them. This should presumably bloat (still on theory here rather than practice) the memory and performance of the service host application due all these assemblies being referenced in mem.
As a result, we should unload -- but the only way to do this is via an appdomain. The thinking is that each assembly would somehow run in its own appdomain and the WCF service is actually just passing messages to the appropriate appdomain. If the appdomain is not used for some_period_of_time, then we simply unload the appdomain.
Some guidance would be useful on:
is this an insane idea?
should the process should run fine with ~100 assemblies in memory?
communication with appdomains would presumably come at some cost (via remoting / named pipes): does this disqualify the idea?
creating an appdomain to basically service one type of .dll would involve many appdomains; is this a bad idea?
I have no experience in this area. My worries are the size and the performance of the app if I don't do something like this. However, with the app domain idea, this basically sounds like massive over-engineering. The requirement to host this unknown .dlls is not something I can change.
Is this idea as bad as it sounds, and what are the pro's / con's associated with it?
should the process run fine with ~100 assemblies in memory?
You'll have to try (it's easy to create a mock-up) but you're only stuck with the code. So at 1 MB a piece you would be using 100MB of discardable memory, I don't expect a problem.
Provided your instances are released and collected.
If you have the memory available and want better performance, you can either wait until the first call is made and the assemblies will be loaded lazily (subsequent calls will be faster), or if you don't want any slow calls made, then you could eager load the assemblies when the service starts. I don't see a reason to load/unload the assemblies on each call when memory is cheap. If you notice a performance problem, then I'd say think about unloading the assemblies when they're not being used.
This is essentially what IIS App Pools and Worker Processes do. Probably not insane, but there's a lot of room for implementation here that could lead to happy or unhappy results.
Related
I originally had a library (workflow engine) and many of its dependency files all copied in the bin of my Web API and I had a direct reference to it. Needless to say, the bin folder looked a mess but it was extremely fast and reliable.
I've recently made changes to our app to load this library and all its dependency files using an AppDomain and while the initial load is a little bit slow (1.5 to 2 secs), every subsequent requests are instant (milli-seconds) which is great as I've changed AutoFac to use a single instance for all the controllers.
Not sure that's recommended and you might throw your 2 cents on this while answering my other questions.
My problem with this implementation is AppDomain are supposed to be unload, whether instantly or not, I'm not sure, but the point is that I've actually commented out that part of the code which unloads it as I don't want it to be unloaded every time otherwise, every request will take 1.5 to 2 secs which would be unacceptable.
Is there a best practise on how to deal with AppDomain with ASP.NET Web API? Is there a way to find out when resources can and should be released efficiently i.e. No API calls in a while, when web api is being unloaded by IIS, other? One of the nasty side effect I've noticed since introducing this AppDomain is that my app is no longer released, which makes sense, unless I do an IISRESET.
Should I be using some other mechanism than app domain? I do need all the dependencies to be loaded as this library heavily relies on them.
No need for code, but if you have it I clearly won't say no :). Just suggestion pointing me in the right direction would be great.
Thanks.
What is the most important use of AppDomains in C#?
The single most important use is that your code has to have one - i.e. everything you write in C# executes in an AppDomain. That is quite important ;-p
If you mean additional app-domains:
When using plugins and other untrusted code, it allows you both isolation, and the ability to unload them (you can't unload assemblies - only entire app-domains).
I'm using it currently to load dynamically generated dlls, so that I can unload them.
They also allow you to set different configuration files, trust levels, etc - but have associated costs of complexity and remoting.
MSDN has a section on app-domains, here.
I can't tell you what the most important use is, since that depends on the situation.
AppDomains are useful for sandboxing parts of your application. You can load extensions in an AppDomain and unload them again - something you cannot otherwise do. You can assign specific rights to AppDomains. Per default objects in different AppDomains cannot access each other.
AppDomains can be viewed as lightweight processes as they give you many of the same features. However, unlike a Process new AppDomains do not have their own thread per default. You have to manage AppDomains and threads yourself.
Also, AppDomains all share the same managed heap. This is usually not a problem, but it may have surprising effects as some instances like strings are shared among AppDomains. For regular use this is not an issue, but if you use strings for locking, threads in different AppDomain can affect each other.
In general, it's not so daily coding practice to use AppDomains, this could be considered something as an advanced concept.. but, starting from this simple thing, it's important to better understand concepts behind the word "AppDomain".
In terms of architecture, and taking it simple as possible, an AppDomain is an isolation container even in terms of memory addressing, inside it all the assemblies needed by an application are loaded and executed, even if this concept is more complicated to explain in details (I hope it's not about yor question to going so deeper).
Starting from there, the AppDomain class first of all is used to obtain access to the application related executing application domain, this could be done via the Singleton property implementation AppDomain.CurrentDomain. In this way it's possible to:
obtain access the loaded assemblies;
obtain access to the appdomain-shared data slots;
intems marshalling, in terms of unwrapping created instances from loaded assemblies in created domains.
Then, the AppDomain class is used to:
create more "domains" in the same process;
executing assemblies in the process;
manage the appdomain's loading/unloading process.
It could be useful to take a view of the code of the new Microsoft framework (not yet released) MEF (Managed Extesibility Framework) which is truly based on concepts like AppDomains creations and unload, dynamically loaded assemblies.
As a simple example of things and examples of what you can do with AppDomains, I can share this link.
I hope I answered your question.
A C# AppDomain is a logically isolated container inside which .NET code run. When you run any .NET code it always runs in a default appdomain.
Do watch this 30 minutes youtube video What is C# AppDomain ? which explains AppDomain in more detail.
But let me still try to explain in more detail. Lets say you get a third party DLL and you want to use it in your application. But you also suspect that the third party can have some malicious code so you would like to run the third party DLL in a constrained environment. Like you do not want the third party to access your c: drive or delete files and so on.
So you can create two AppDomains one which is for the third party and one for your own C# classes. For the third party appdomain you will apply security constraint that it can not access c: drive and for your C# DLLs you will have a unrestricted app domain.
Please read my blog for standard application of runtime loading of DLLs and cross-communication using AppDomain. https://blog.vcillusion.co.in/sending-events-through-application-domain-boundary/
Runtime Loading and unloading of DLLs: I worked on a project where DLLs are loaded at runtime by the user, and during program execution, the methods are executed using Reflection and unloaded during the program run.
Securing my Main Execution Program: We are loading the DLL dynamically so any exception that occurred in that dynamically loaded DLL didn't affect my main AppDomain. In case of corruption scenarios, we have the option of efficiently unloading and loading the DLL again.
Cross-AppDomain Communication: We can dynamically load any two DLLs at runtime in different AppDomain and make them communicate with each other.
I have Background Job Engine that runs jobs. It could be 50 jobs at the same time. All run in a single AppDomain, different threads.
The problem is that it is impossible to:
Kill a job (killing a thread is not an option)
To get job's memory usage
Theoretically, the solution is running each job in its own AppDomain, but having 50 AppDomains is impractical. AppDomains are heavy and one of the reasons - they load all the assemblies, even if they are all the same.
Now I realize there is no solution from within the .NET realm, but what if my Background Job Engine was a native C++ application hosting the .NET run-time. Do I have more options there? Could I implement a sort of "light" AppDomain that would enable me to run 50 jobs each in its own "light" AppDomain?
A "light" AppDomain would use the shared set of .NET assemblies and should come up in a snap. But it still should give a fair amount of isolation, enough to allow me to bring it down along with everything running inside it. In this case, only assemblies loaded directly in this AppDomain would be unloaded. It would also be great to be able to collect the memory usage of individual "light" AppDomain.
Any ideas?
EDIT 1
Suppose that I could run 50 AppDomains and I am OK with whatever control that gives my over the code running in an AppDomain. Now we all understand that running 50 AppDomains is unrealistic.
But what makes it so unrealistic? I can think of one reason - the necessity to load all the assemblies, could be other reasons as well.
But why is it working like this?
Isn't it true that the assembly code is read-only?
Or is it because of the static variables that are mapped within the assembly code space?
In short, what makes the model where different AppDomains share the some assemblies so problematic?
I am not familiar with .NET runtime hosting, so I am curious whether the shared assemblies model could be implemented manually through usage of some advanced .NET runtime hosting API. In that model, the host preloads some assemblies which are shared by all the AppDomains.
I can think of at least two possible .NET-only solutions, with various trade-offs for each.
Each job gets its own thread (or use async/await if you want to reduce threads). Each job has a Cancel() method that sets both a boolean flag and a ManualResetEvent. Then during any async operation, you use WaitHandle.WaitAny(workEvent, cancelEvent) which gives you the option to cancel the current async work. At various points in your code you can also check whether the cancel boolean flag is set, and terminate the job if it is. You can optionally do cleanup after cancellation if desired. When your jobs allocate or deallocate resources, manually keep a running counter of the subset of resources which are likely to consume the most memory, as a rough approximation of memory usage. This won't give you exact numbers, but relative to other jobs it should help you identify the worst offenders, and could give you more detailed insight into why they're consuming memory.
Break this into two .NET executables: a job controller, and a worker. The job controller can create as many worker processes as it wants using Process.Start(), and can track exact memory consumption for each Process. It can also terminate individual processes and be guaranteed that all resources will be freed. Downsides: additional process overhead, and potential messiness left behind if cleanup is needed.
Edit: a few more options
Implement your jobs as Tasks and use CancellationTokens.
Use something like Hangfire to manage your jobs. It supports cancellation tokens.
If we create a singleton class in a dll and use it in another dll which instantiates this singleton class, and an exe uses this dll at a point of time, will these two dlls get unloaded immediately after their job or will they get unloaded only when the main exe exits?
Disclaimer: I am not an expert on memory or processes/app domains, so I may have some specifics wrong, but I firmly believe that my conclusion is valid - feel free to comment and correct any specific errors.
No, the DLL is not unloaded until the app domain that contains it (typically the main process for windows/console apps and the web site for ASP.NET apps) is closed.
From MSDN:
There is no way to unload an individual assembly without unloading all of the application domains that contain it.
So you can manually create (and destroy) app domains, which will unload any DLLs, but then you have to marshal data across the domain boundaries, which may be worse than just keeping the DLL in memory.
The objects created by the DLL may be removed from memory (if nothing holds a reference to them), but the code itself will remain in memory. In the case of a pure singleton, it will stay in memory until the app closes (since the class that manages it will always have a reference). You could remove the reference to the singleton from the factory class, but you still may have other code that references it, so you risk having multiple instances of your "singleton". If that's not a concern, that may be a valid strategy to dispose of the singleton when you're done with it.
Think about most apps - they respond to human interaction in some way. Users can click any button, select any menu item, etc. in any order that the app allows. How is an application supposed to know for certain that code within a DLL will never get called again? Assemblies are relatively expensive to load (in part since they must be JITted), so it would be inefficient to unload them if there's any chance that they'll get loaded again.
Bottom line - since the memory footprint of a DLL is usually relatively small (since it just contains code and metadata) and is only loaded once for an entire domain, unless you experience significant memory pressure from it that can't me relived by managing other memory better, I wouldn't go to the trouble of trying to unload it.
What is the most important use of AppDomains in C#?
The single most important use is that your code has to have one - i.e. everything you write in C# executes in an AppDomain. That is quite important ;-p
If you mean additional app-domains:
When using plugins and other untrusted code, it allows you both isolation, and the ability to unload them (you can't unload assemblies - only entire app-domains).
I'm using it currently to load dynamically generated dlls, so that I can unload them.
They also allow you to set different configuration files, trust levels, etc - but have associated costs of complexity and remoting.
MSDN has a section on app-domains, here.
I can't tell you what the most important use is, since that depends on the situation.
AppDomains are useful for sandboxing parts of your application. You can load extensions in an AppDomain and unload them again - something you cannot otherwise do. You can assign specific rights to AppDomains. Per default objects in different AppDomains cannot access each other.
AppDomains can be viewed as lightweight processes as they give you many of the same features. However, unlike a Process new AppDomains do not have their own thread per default. You have to manage AppDomains and threads yourself.
Also, AppDomains all share the same managed heap. This is usually not a problem, but it may have surprising effects as some instances like strings are shared among AppDomains. For regular use this is not an issue, but if you use strings for locking, threads in different AppDomain can affect each other.
In general, it's not so daily coding practice to use AppDomains, this could be considered something as an advanced concept.. but, starting from this simple thing, it's important to better understand concepts behind the word "AppDomain".
In terms of architecture, and taking it simple as possible, an AppDomain is an isolation container even in terms of memory addressing, inside it all the assemblies needed by an application are loaded and executed, even if this concept is more complicated to explain in details (I hope it's not about yor question to going so deeper).
Starting from there, the AppDomain class first of all is used to obtain access to the application related executing application domain, this could be done via the Singleton property implementation AppDomain.CurrentDomain. In this way it's possible to:
obtain access the loaded assemblies;
obtain access to the appdomain-shared data slots;
intems marshalling, in terms of unwrapping created instances from loaded assemblies in created domains.
Then, the AppDomain class is used to:
create more "domains" in the same process;
executing assemblies in the process;
manage the appdomain's loading/unloading process.
It could be useful to take a view of the code of the new Microsoft framework (not yet released) MEF (Managed Extesibility Framework) which is truly based on concepts like AppDomains creations and unload, dynamically loaded assemblies.
As a simple example of things and examples of what you can do with AppDomains, I can share this link.
I hope I answered your question.
A C# AppDomain is a logically isolated container inside which .NET code run. When you run any .NET code it always runs in a default appdomain.
Do watch this 30 minutes youtube video What is C# AppDomain ? which explains AppDomain in more detail.
But let me still try to explain in more detail. Lets say you get a third party DLL and you want to use it in your application. But you also suspect that the third party can have some malicious code so you would like to run the third party DLL in a constrained environment. Like you do not want the third party to access your c: drive or delete files and so on.
So you can create two AppDomains one which is for the third party and one for your own C# classes. For the third party appdomain you will apply security constraint that it can not access c: drive and for your C# DLLs you will have a unrestricted app domain.
Please read my blog for standard application of runtime loading of DLLs and cross-communication using AppDomain. https://blog.vcillusion.co.in/sending-events-through-application-domain-boundary/
Runtime Loading and unloading of DLLs: I worked on a project where DLLs are loaded at runtime by the user, and during program execution, the methods are executed using Reflection and unloaded during the program run.
Securing my Main Execution Program: We are loading the DLL dynamically so any exception that occurred in that dynamically loaded DLL didn't affect my main AppDomain. In case of corruption scenarios, we have the option of efficiently unloading and loading the DLL again.
Cross-AppDomain Communication: We can dynamically load any two DLLs at runtime in different AppDomain and make them communicate with each other.