3

I have a C# application which launches another executable using Process.Start().

99% of the time this call works perfectly fine. After the application has run for quite some time though, Process.Start() will fail with the error message:

Insufficient system resources exist to complete the requested service

Initially I thought this must have been due to a memory leak in my program - I've profiled it fairly extensively and it doesn't appear there's a leak - the memory footprint will still be reasonable even when this message failed.

Immediately after a failure like this, if I print some of the system statistics it appears that I have over 600MB of RAM free, plenty of space on disk, and the CPU usage is effectively at 0%.

Is there some other system resource I haven't thought of? Am I running into a memory limit within the .NET VM?

Edit2:

I opened up the application in SysInternals Process Explorer and it looks like I'm leaking Handles left and right:

Handles Used: 11,950,352 (!)
GDI Handles: 26
USER Handles: 22

What's strange here is that the Win32 side of handles seem very reasonable, but somehow my raw handle count has exploded waaaaay out of control. Any ideas what could cause a Handle leak like this? I was originally convinced it was Process.Start() but that would be USER handles, wouldn't it?

Edit:

Here's an example of how I'm creating the process:

var pInfo = new ProcessStartInfo(path, ClientStartArguments)
        {
            UseShellExecute = false,
            WorkingDirectory = workingDirectory
        };    
ClientProcess = Process.Start(pInfo);

Here's an example of how I kill the same process (later in the program after I have interacted with the process):

Process[] clientProcesses = Process.GetProcessesByName(ClientProcessName);
if (clientProcesses.Length > 0)
{
     foreach (var clientProcess in clientProcesses.Where(
              clientProcess => clientProcess.HasExited == false))
     {
        clientProcess.Kill();
     }
}
mweber
  • 1,292
  • 2
  • 10
  • 13
  • 1
    What kind of process are you opening, console or windows app? Are you opening many processes? – Slugart Mar 22 '12 at 18:10
  • It's a windows application. Occasionally my application will restart this same application by calling Process.Kill() and relaunching it with Process.Start(). This is the only application that is opened with Process.Start() though. – mweber Mar 22 '12 at 18:13
  • 2
    Are you calling Dispose on the instances? If not you may be leaking handles. – Brian Rasmussen Mar 22 '12 at 18:14
  • 2
    Even though you might not be leaking memory you might be leaking window handles which are needed to instiate new windows apps. – Slugart Mar 22 '12 at 18:15
  • Wow, I don't call Process.Dispose() - I'm guessing I should be doing this after every Process.Kill() call? – mweber Mar 22 '12 at 18:15
  • 1
    Process.Start is a static method so he can't dispose it. Also even if he was starting an instance of Process it would not own references to window handles in a different process. – Slugart Mar 22 '12 at 18:20
  • I do later get a handle on the Process using Process.GetProcessesByName() and then call Kill(). I can try calling Dispose() after I call Kill() on the Process though. – mweber Mar 22 '12 at 18:23

3 Answers3

4

The problem here is with retained process handles. As we can see from your later edits you are keeping a reference to the Process object returned by Process.Start(). As mentioned in the documentation of Process:

Like many Windows resources, a process is also identified by its handle, which might not be unique on the computer. A handle is the generic term for an identifier of a resource. The operating system persists the process handle, which is accessed through the Handle property of the Process component, even when the process has exited. Thus, you can get the process's administrative information, such as the ExitCode (usually either zero for success or a nonzero error code) and the ExitTime. Handles are an extremely valuable resource, so leaking handles is more virulent than leaking memory.

I especially like the use of the word virulent. You need to dispose and release the reference to Process.

Also check out this excellent question and it's corresponding answer: Not enough memory or not enough handles?

Community
  • 1
  • 1
Slugart
  • 4,535
  • 24
  • 32
2

Since the Process class implements IDisposable, it is good practice to properly dispose of it when you are done. In this case, it will prevent handle leaks.

using (var p = new Process())
{
    p.StartInfo = new ProcessStartInfo(@"C:\windows\notepad.exe");
    p.Start();
    p.WaitForExit();
}

If you are calling Process.Kill() and the process has already exited, you will get an InvalidOperationException.

Bryan Crosby
  • 6,486
  • 3
  • 36
  • 55
  • My application requires opening this second application and then letting it run while I manipulate it. There's not a particular point where I want to block waiting for the exit. – mweber Mar 22 '12 at 18:25
  • @mweber Are you currently calling the static overload of Process.Start? – Slugart Mar 22 '12 at 18:47
  • @Slugart Yeah, I use the Process.Start() static overload - I've got an example of how I create/kill processes in my program in the question now. – mweber Mar 22 '12 at 19:00
  • @mweber as you are using the static method and not the instance method you will not be able to, nor do you need to call Dispose. I would look to see how many window handles your main application is consuming during it's lifetime and when it fails to open the process. If there's any other information you can give us it would be helpfull. – Slugart Mar 22 '12 at 19:05
  • @Slugart Is there a way to measure window handles from within .NET or will I need to just be counting the number of Process.Start() calls I make? – mweber Mar 22 '12 at 21:04
  • 1
    @mweber check out this question and it's excellent answer: http://stackoverflow.com/questions/2670574/not-enough-memory-or-not-enough-handles – Slugart Mar 23 '12 at 09:25
  • @Slugart I found some more details out after reading that thread you linked to - long story short I've got a very nasty Handle leak somewhere, but I don't think it's related to Process.Start() - that's just the most obvious thing which is failing due to handle exhaustion. – mweber Mar 23 '12 at 18:42
  • @Slugart Any chance you can throw an answer on here about hunting down handle leaks / referencing that thread? I found a place in my code where I was getting the process handle and not closing it in a very commonly called function, hence the Handle explosion. The issue is resolved and I'd like to give you credit for the answer. – mweber Mar 23 '12 at 19:58
1

That's not an uncommon problem to have with little programs like this. The problem is that you are using a large amount of system resources but very little memory. You don't put enough pressure on the garbage collected heap so the collector never runs. So finalizable objects, the wrappers for system handles like Process and Thread, never get finalized.

Simply disposing the Process object after the process has exited will go a long way to solve the problem. But might not solve it completely, any threads that the Process class uses or you use yourself consume 5 operating system handles each. The Thread class doesn't have a Dispose() method. It should but it doesn't, it is next to impossible to call it correctly.

The solution is triggering a garbage collection yourself. Count the number of times you start a process. Every, say, hundredth time call GC.Collect(). Keep an eye on the Handle count with Taskmgr.exe. Use View + Select Columns to add it. Fine tune the GC.Collect calls to so that it doesn't increase beyond, say, 500.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536