You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

获取已退出子进程内存使用情况遇System.InvalidOperationException异常求助

解决进程退出后无法获取PeakWorkingSet64的问题

咱先搞清楚你为啥遇到这个报错:当进程彻底退出后,系统会立刻回收它的大部分内核资源,这时候System.Diagnostics.Process对象就没法再读取PeakWorkingSet64这类性能数据了——这就是那个InvalidOperationException的根源。

既然你对准确性和效率要求很高,轮询肯定不是最优解,下面两个非轮询的方案完全符合你的需求:

方案1:监听进程退出事件,在资源回收前瞬间捕获峰值

这个方案是事件驱动的,完全不需要轮询,效率拉满。核心思路是给子进程注册Exited事件,在进程刚退出但系统还没来得及回收资源的瞬间,立刻读取PeakWorkingSet64

直接上代码示例:

using System;
using System.Diagnostics;

class ProcessMemoryTracker
{
    private static long _peakMemoryBytes;

    static void Main(string[] args)
    {
        // 配置子进程启动信息
        var startInfo = new ProcessStartInfo("your-child-process.exe")
        {
            UseShellExecute = false,
            RedirectStandardOutput = true
        };

        using var childProcess = new Process();
        childProcess.StartInfo = startInfo;
        childProcess.EnableRaisingEvents = true; // 必须开启事件触发,否则Exited事件不会生效
        childProcess.Exited += OnChildProcessExited;

        // 启动子进程
        childProcess.Start();
        // 如果需要等待子进程完成,这里可以用WaitForExit,但Exited事件会在退出时自动触发
        childProcess.WaitForExit();

        Console.WriteLine($"子进程峰值工作集内存:{_peakMemoryBytes} 字节");
    }

    private static void OnChildProcessExited(object sender, EventArgs e)
    {
        if (sender is Process process)
        {
            try
            {
                // 此时进程刚退出,资源还没被回收,能成功读取峰值内存
                _peakMemoryBytes = process.PeakWorkingSet64;
            }
            catch (Exception ex)
            {
                // 极端场景下可能出现异常,这里可以加容错逻辑
                Console.WriteLine($"捕获峰值内存失败:{ex.Message}");
            }
        }
    }
}

这里的关键是EnableRaisingEvents = true——如果不设置这个,Exited事件根本不会触发,一定要记得加。

方案2:用Performance Counter跟踪(支持进程退出后读取)

如果你需要更全面的内存监控,或者担心事件触发时机的极端情况,可以用PerformanceCounter。它会保留进程的统计数据一段时间,哪怕进程退出了,也能读到峰值内存。

示例代码:

using System;
using System.Diagnostics;

class PerformanceCounterMemoryTracker
{
    static void Main(string[] args)
    {
        using var childProcess = Process.Start("your-child-process.exe");
        if (childProcess == null)
        {
            Console.WriteLine("启动子进程失败");
            return;
        }

        // 创建针对当前子进程的峰值工作集计数器
        // 注意:如果有同名进程,要用进程ID作为实例名,避免混淆
        using var peakCounter = new PerformanceCounter(
            categoryName: "Process",
            counterName: "Peak Working Set",
            instanceName: childProcess.Id.ToString(),
            readOnly: true
        );

        // 等待子进程退出
        childProcess.WaitForExit();

        // 进程退出后仍能读取计数器的值
        long peakMemoryBytes = (long)peakCounter.NextValue();
        Console.WriteLine($"子进程峰值工作集内存:{peakMemoryBytes} 字节");
    }
}

这个方案的优势是哪怕进程退出一小会儿,只要系统还没清理掉性能计数器的缓存,就能拿到数据,容错性更强。

最后提几个注意点

  • 不管用哪个方案,都要记得用using语句或者手动调用Dispose()释放ProcessPerformanceCounter对象,避免资源泄漏。
  • 如果你用的是.NET Core/.NET 5+,这些API完全兼容,跨平台也能用。
  • 方案1的事件触发时机非常早,几乎不会出现拿不到数据的情况,是效率最高的选择。

内容的提问来源于stack exchange,提问作者victor12369

火山引擎 最新活动