简体   繁体   中英

Loading an assembly into an AppDomain but also in the AppDomain.CurrentDomain resulting in memory leaks

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.

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 .

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...

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM