Wednesday, December 10, 2003 6:10 PM
richard
Use an AppDomain to run code (and Replace GC.Collect calls)
Most .NET developers, at some point, get the urge to call GC.Collect() - maybe we just think our app is taking up too much memory (even though we know calling GC.Collect and WaitForPendingFinalizers will not automatically reduce the WorkingSet of our process) or we get the 'urge to purge' our heap before we start some new memory-intensive procedure. (Even in the off chance that GC.Collect() is the right thing to do, you still face being chastised for using it.)
Here I offer a alternative suitable for *some* cases where you might want to deterministically reclaim some memory. It's a utility I arbitrarily named “DomainRunner” because it allows running any method in a temporary AppDomain, separate from your main Domain.
First, here's a faux sample that uses a lot of memory:
public void useLotsOfMemory()
{
Random rand = new Random();
System.Collections.ArrayList biglist = new System.Collections.ArrayList();
for(int i=0;i<4000000;i++)
{
biglist.Add((decimal)rand.NextDouble());
}
result=(decimal)biglist[biglist.Count-1];
Console.WriteLine("did it...");
}
In this sample we create an box a bunch of decimal types (which are larger than doubles memory-wise) thus sucking up precious memory. Enter the DomainRunner class. It defines two methods and a nested class:
using System;
namespace RunCodeInAppDomain
{
public class DomainRunner
{
public static object RunInAppDomain(System.Delegate delg, AppDomain targetDomain, params object[] args)
{
domainDomainRunner runner = new domainDomainRunner(delg, args, delg.GetHashCode());
targetDomain.DoCallBack(new CrossAppDomainDelegate(runner.Invoke));
return targetDomain.GetData("appDomainResult" + delg.GetHashCode());
}
public static object RunInAppDomain(System.Delegate delg, params object[] args)
{
AppDomain tempDomain = AppDomain.CreateDomain("domain_RunInAppDomain" + delg.GetHashCode());
object returnValue=null;
try
{
returnValue = RunInAppDomain(delg, tempDomain, args);
}
finally
{
AppDomain.Unload(tempDomain);
}
return returnValue;
}
[Serializable]
internal class domainDomainRunner
{
private System.Delegate _delegate;
private object[] _arguments;
public int _hash=0;
public domainDomainRunner(System.Delegate delg, object[] args, int hash)
{
_delegate=delg;
_arguments=args;
_hash=hash;
}
public void Invoke()
{
Console.WriteLine("I'm running in domain named {0}", AppDomain.CurrentDomain.FriendlyName);
if(_delegate!=null)
AppDomain.CurrentDomain.SetData("appDomainResult" + _hash, _delegate.DynamicInvoke(_arguments));
}
}
}
}
I'm not certain exactly how practical these methods are for the real world, but in my toy problems they worked great! If nothing else, this shows you a few things:
1) How to create and unload an AppDomain
2) How to run code in another AppDomain
3) How to run arbitrary methods via a delegate (and in another AppDomain at that)
Bon chance!