Process.Start失败是因为“系统资源不足”

时间:2012-03-22 18:07:43

标签: c# .net memory

我有一个C#应用程序,它使用Process.Start()启动另一个可执行文件。

99%的时间此通话完全正常。应用程序运行了一段时间后,Process.Start()将失败并显示错误消息:

Insufficient system resources exist to complete the requested service

最初我认为这一定是由于我的程序中存在内存泄漏 - 我已经对它进行了相当广泛的分析并且看起来没有泄漏 - 即使此消息失败,内存占用仍然是合理的。 / p>

在出现这样的故障之后,如果我打印出一些系统统计数据,看起来我有超过600MB的RAM空间,磁盘上有足够的空间,CPU使用率实际上是0%。

还有其他一些我没想过的系统资源吗?我是否在.NET VM中遇到了内存限制?

EDIT2:

我在SysInternals Process Explorer中打开了应用程序,看起来我正在向左和向右泄漏句柄:

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

这里的奇怪之处在于Win32一侧的句柄看起来非常合理,但不知何故,我的原始句柄数量已经爆发,并且已经失控。任何可能导致Handle泄漏的想法都是这样的吗?我原本确信它是Process.Start(),但那将是USER句柄,不是吗?

编辑:

以下是我如何创建流程的示例:

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

以下是我如何杀死同一进程的示例(在我与该进程交互后的程序中稍后):

Process[] clientProcesses = Process.GetProcessesByName(ClientProcessName);
if (clientProcesses.Length > 0)
{
     foreach (var clientProcess in clientProcesses.Where(
              clientProcess => clientProcess.HasExited == false))
     {
        clientProcess.Kill();
     }
}

3 个答案:

答案 0 :(得分:4)

这里的问题是保留的进程句柄。正如我们从后面的编辑中可以看到的那样,您将保留对Process.Start()返回的Process对象的引用。正如流程的documentation中提到的那样:

  

与许多Windows资源一样,进程也由其句柄标识,该句柄在计算机上可能不是唯一的。句柄是资源标识符的通用术语。操作系统持久保存进程句柄,即使进程已退出,也可通过Process组件的Handle属性访问该句柄。因此,您可以获取进程的管理信息,例如ExitCode(通常为成功为零或非零错误代码)和ExitTime。手柄是非常有价值的资源,因此泄漏的手柄比泄漏记忆更具毒性。

我特别喜欢使用毒性这个词。您需要处理并释放对Process的引用。

另请查看这个优秀的问题及其相应的答案:Not enough memory or not enough handles?

答案 1 :(得分:2)

由于Process类实现了IDisposable,因此在完成后正确处理它是一种好习惯。在这种情况下,它将防止手柄泄漏。

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

如果您致电Process.Kill()并且该流程已退出,您将获得InvalidOperationException

答案 2 :(得分:1)

这对于像这样的小程序来说并不是一个不常见的问题。问题是您使用的是大量系统资源但内存很少。您没有对垃圾收集堆施加足够的压力,因此收集器永远不会运行。所以可终结的对象,系统句柄的包装器,如Process和Thread,永远不会最终确定。

在流程退出后简单地处理Process对象将大大有助于解决问题。但是可能无法完全解决它,Process类使用或您自己使用的任何线程都会消耗5个操作系统句柄。 Thread类没有Dispose()方法。应该但事实并非如此,几乎不可能正确地调用它。

解决方案是自己触发垃圾回收。计算启动流程的次数。每个,比方说,第100次调用GC.Collect()。使用Taskmgr.exe密切关注句柄计数。使用视图+选择列添加它。微调GC.Collect调用,使其不会增加,例如500。

相关问题