Reputation: 2620
I have to execute some line which comes from a jscript into another appdomain than the current one. For this i have the following piece of code.
AppDomain ad = null;
try
{
ad = AppDomain.CreateDomain("new AD" + new Random(), null, null);
ad.CreateInstanceAndUnwrap
(
assembly,
type,
true,
BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.CreateInstance,
null,
args,
null,
null,
null
);
}
catch (Exception e)
{
throw new Exception("Automated script engine had an error. " + e.Message + "\n" + e.InnerException.StackTrace + "\n" + e.HelpLink);
}
finally
{
if (ad != null)
{
Assembly[] a = ad.GetAssemblies();
Console.WriteLine(a.Length);
Assembly[] mainAssemblies = AppDomain.CurrentDomain.GetAssemblies();
Console.WriteLine(mainAssemblies.Length);
AppDomain.Unload(ad);
GC.AddMemoryPressure(GC.GetTotalMemory(true));
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
GC.Collect();
ad = null;
}
}
}
But when I inspect all assemblies loaded into the current AppDomain (via AppDomain.CurrentDomain.GetAssemblies()
) my asembly is also loaded.
And as i might have to run let's say 2000 tests
, i understand that 2000 assemblies
will be loaded into my CurrentDomain
.
Is there a way to load this assembly into an AppDomain but without adding it to the CurrentDomain ?
The method calls in the finally
block are just my tries over here. Don't judge me for those if they are not useful maybe.
Upvotes: 0
Views: 915
Reputation: 4172
The culprit of the leak is:
Assembly[] a = ad.GetAssemblies();
You cannot pass assemblies from one AppDomain to another without loading them. So, although you have initially loaded the assembly into "ad", the call AppDomain.GetAssemblies
will load them to the current AppDomain (not the "ad" variable).
The easiest way to overcome this problem is to add a method to your MarshalByRefObject derived class (the type
variable in your sample) that returns AssemblyName
objects which will not cause such a leak to the main AppDomain.
public AssemblyName[] GetAssemblyNames()
{
return AppDomain.CurrentDomain
.GetAssemblies()
.Select(asm => asm.GetName()).ToArray();
}
And instead of:
Assembly[] a = ad.GetAssemblies();
you do:
AssemblyNames[] a = someRemoteObject.GetAssemblyNames();
where someRemoteObject
is the return value from the call to CreateInstanceAndUnwrap
.
Upvotes: 2
Reputation: 2806
First you have to learn that you cannot unload any assembly when it is loaded into you appdomain.
To ensure that the assembly is not loaded in you main
app domain, you need remoting.
This means your type
must derive from MarshalByRefObject
and must be Serializable
.
The GC
is not responsible to unload or load any assembly. The garbage collector ensures memeory clean up for managed memory.
Upvotes: 0
Reputation: 11607
IIRC you can't unload an assembly in .NET 2.0, but correct me if it's possible in later versions, I didn't check that out. So, be sure it's possible because once upon a time unloading was impossible.
Second, you need 2000 different FILES to have 2000 different assemblies. Maybe you mean to load the same assembly 2000 times? In that case, you really will have only one assembly in memory.
There is a way to only load the reflection data into memory without actually loading the assembly, but this is not what you want.
The GC won't help much here. Look into test driven development for more pointers...
Upvotes: 0