【问题标题】:What does it mean when Process.Start fails because "Insufficient system resources exist"Process.Start因为“系统资源不足”而失败是什么意思
【发布时间】:2012-04-07 08:28:35
【问题描述】:

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

这个调用在 99% 的情况下都能正常工作。但是,在应用程序运行了一段时间后,Process.Start() 将失败并显示错误消息:

Insufficient system resources exist to complete the requested service

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

在发生这样的故障后,如果我立即打印一些系统统计信息,我似乎有超过 600MB 的可用 RAM、足够的磁盘空间,并且 CPU 使用率实际上是 0%。

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

编辑2:

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

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

这里奇怪的是,Win32 端的句柄看起来很合理,但不知何故,我的原始句柄数已经爆炸 waaaaay 失控了。有什么想法会导致这样的句柄泄漏吗?我最初确信它是 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();
     }
}

【问题讨论】:

  • 你打开的是什么进程,控制台还是windows应用?您是否打开了许多进程?
  • 这是一个 Windows 应用程序。有时,我的应用程序会通过调用 Process.Kill() 并使用 Process.Start() 重新启动它来重新启动同一个应用程序。这是唯一使用 Process.Start() 打开的应用程序。
  • 您是否在实例上调用 Dispose?如果不是,您可能正在泄漏句柄。
  • 即使您可能没有泄漏内存,您也可能会泄漏启动新 Windows 应用程序所需的窗口句柄。
  • Process.Start 是一个静态方法,所以他无法处理它。此外,即使他正在启动 Process 的实例,它也不会拥有对不同进程中窗口句柄的引用。

标签: c# .net memory


【解决方案1】:

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

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

我特别喜欢使用“恶毒”这个词。您需要处置和释放对 Process 的引用。

还可以查看这个优秀的问题及其对应的答案:Not enough memory or not enough handles?

【讨论】:

    【解决方案2】:

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

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

    如果您正在调用Process.Kill() 并且进程已经退出,您将获得InvalidOperationException。

    【讨论】:

    • 我的应用程序需要打开第二个应用程序,然后在我操作它时让它运行。我没有特别想阻止等待出口的地方。
    • @mweber 你当前是在调用 Process.Start 的静态重载吗?
    • @Slugart 是的,我使用 Process.Start() 静态重载 - 我现在有一个示例说明如何在我的程序中创建/杀死进程。
    • @mweber 因为您使用的是静态方法,而不是您无法使用的实例方法,也不需要调用 Dispose。我会查看您的主应用程序在其生命周期内以及何时无法打开进程时消耗了多少窗口句柄。如果您有任何其他信息可以提供给我们,将会很有帮助。
    • @mweber 看看这个问题,这是一个很好的答案:stackoverflow.com/questions/2670574/…
    【解决方案3】:

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

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

    解决方案是自己触发垃圾收集。计算启动进程的次数。比如说,每百次调用 GC.Collect()。使用 Taskmgr.exe 密切关注句柄计数。使用 View + Select Columns 添加它。微调 GC.Collect 调用,使其不会超过 500。

    【讨论】:

      猜你喜欢
      • 2011-02-21
      • 1970-01-01
      • 1970-01-01
      • 2015-01-22
      • 2015-06-10
      • 1970-01-01
      • 2011-05-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多