5

I have an unusual requirement for some code I am working on. I'm using an unreliable 3rd party library to do some barcode scanning (it stops working after running too many times). To get around this problem I decided to do the work in a separate AppDomain and then to unload the AppDomain when I am finished. This is a simplistic, but accurate, picture of what I am doing:

string domainID = Guid.NewGuid().ToString();
AppDomainSetup setup = new AppDomainSetup();
AppDomain domain = AppDomain.CreateDomain(domainID, null, setup);

string result = null;
try
{
    domain.SetData("stream", stream);
    domain.DoCallBack(ScanningContext.DoWork);

    result = domain.GetData("result") as string;
}
finally
{
    AppDomain.Unload(domain);
}

return result;

public static void DoWork()
{
    Stream s = AppDomain.CurrentDomain.GetData("stream") as Stream;
    ObjectHandle handle = AppDomain.CurrentDomain.CreateInstance("Scanning",
        "Scanner");

    Scanning.Scanner scanner = (Scanning.Scanner)handle.Unwrap();
    Scanning.Result[] results = scanner.Scan(s);

    AppDomain.CurrentDomain.SetData("result", results[0].Text);
}

"Scanner" is a wrapper class around the library I'm using. It sits in the "Scanning" assembly; a separate project just for this purpose.

ScanningContext.DoWork is a static method that sits in my service's assembly.

My problem with this method is that there is a memory leak some where. Memory keeps growing and growing (when this code is called, of course) until OutOfMemoryExceptions are thrown.

I can't find the leak any where. All of my streams are being disposed. All of my byte arrays are being nulled. I'm clearing lists, everything that has worked for me in the past. I'm about 90% confident that the leak is related to this AppDomain stuff. This is my first time using it so I'm probably doing something wrong.

I'm open to another approach besides AppDomains. I do require the ability to return results from the "Scanner" class, so spawning a process is not an option.

Matthew
  • 11,203
  • 9
  • 38
  • 44
  • You say you can run the 3rd party code at least a couple of times. So can you rule out the last 10% uncertainty by checking if the leak also exists w/o using app domains? – Christian.K Feb 07 '12 at 14:13
  • 2
    Remove the scanner calling code, return a fixed result and check if the memory leaks still there then its appDomain issue, otherwise its the 3rd party library issue.. –  Feb 07 '12 at 14:21
  • The old method consumes a lot of memory, about 250,000k as I look at it now, so it's memory use is not good. However it hasn't ran out of memory like my new approach is doing. So to answer your question, no I can't rule out the other 10%, I did have to rewrite a bit of code to use AppDomains so I could have introduced the leak at that time. – Matthew Feb 07 '12 at 14:21
  • Thanks @HaLaBi: Since I'm unloading the domain, shouldn't the memory be freed? – Matthew Feb 07 '12 at 14:21
  • I usually use deleaker tool in similar cases... – MastAvalons Feb 07 '12 at 14:24
  • 2
    Use the debugging tools for windows to capture a memory dump of the app when it hits the out of memory - then find out *which* objects are actually leaking. At the moment, it sounds like you're guessing, which is never a good way to address these kind of issues (i.e. the above code looks reasonable on a cursory reading, you may not be showing us any code relevant to the actual leak) – Damien_The_Unbeliever Feb 07 '12 at 14:29
  • Thanks @Damien_The_Unbeliever, I'm sure you're right. Which debugging tools are you referring to? I'm tried .NET Memory Profiler 4.0 but it doesn't give me useful information (too much noise, not specific about objects). – Matthew Feb 07 '12 at 14:32
  • [Debugging tools for Windows](http://msdn.microsoft.com/en-us/windows/hardware/gg463009). The specific tool you want to use is called adplus. Once you've got the dump file, you'll use WinDBG to load the dump file, and SOS (Son of Strike) to analyse it. Search [Tess Ferrandez's Blog](http://blogs.msdn.com/b/tess/) for more tips. – Damien_The_Unbeliever Feb 07 '12 at 14:34
  • @Matthew If your 3rd party library has a problem which cause memory leaks (like unmanaged code or I/O problems) this could be the reason for your AppDomain not to Unload properly. `in the .NET Framework version 2.0 domain is not guaranteed to unload, because it might not be possible to terminate executing threads.` [From MSDN](http://msdn.microsoft.com/en-us/library/system.appdomain.unload.aspx) –  Feb 07 '12 at 14:34
  • I'm curious, why would a requirement to get the scanning results mean you can't spawn another process? Can't you just serialize the results in a file (or [standard output](http://stackoverflow.com/a/285841/533120), or socket etc....)? – Branko Dimitrijevic Feb 07 '12 at 15:55
  • @BrankoDimitrijevic the result object includes a byte array. Sending things to files adds a lot of room for error, sockets are out of the question due to this process running in multiple threads. – Matthew Feb 07 '12 at 17:16
  • 1
    Maybe I'm missing something, but why would writing a bunch of bytes into a file leave "a lot of room for error"? BTW, monitoring the standard output should work for multiple threads/processes. Ditto for sockets, under the condition you supply a unique port number to each process (e.g. through a command-line argument). You could even use a more exotic inter-process communication channel, such as database or message queue. – Branko Dimitrijevic Feb 07 '12 at 18:20

1 Answers1

2

The AppDomain.Unload method starts a separate thread to unload the domain, which may fail for various reasons (threads executing unmanaged code is a problem). Here is a sample code that checks if the app domain is unloaded (taken from msdn docs):

 try
 {
 Console.WriteLine();
 // Note that the following statement creates an exception because the domain no longer exists.
 Console.WriteLine("child domain: " + domain.FriendlyName);
 } 
 catch (AppDomainUnloadedException e)
 {
 Console.WriteLine("The appdomain MyDomain does not exist.");
 }
muratgu
  • 7,241
  • 3
  • 24
  • 26