You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Popen内存处理机制问询及高频调用内存累积问题排查

Popen调用内存升高问题分析与解决方案

核心疑问解答

子进程运行到代码末尾确实会自动终止,但这不等于父进程持有的子进程相关资源会自动释放,这是你遇到内存累积的核心原因。

subprocess模块内存/资源处理逻辑

  • 每次创建Popen实例时,Python会向操作系统申请子进程的运行资源,同时在内核层为父进程分配子进程的控制句柄,用户态的Popen对象会持有该句柄引用
  • 子进程退出后,操作系统会保留子进程的退出状态、少量基础资源,直到父进程调用wait()/communicate()等接口主动回收,这部分资源才会完全释放
  • 若没有主动调用回收接口,即使Popen对象被Python GC回收,部分平台(尤其是Windows)的内核句柄也可能出现延迟释放,甚至长期泄漏的情况
  • 如果你创建Popen时指定了stdout=subprocess.PIPE/stderr=subprocess.PIPE但没有读取管道内容,还会导致管道缓冲区内存泄漏,同时可能卡住子进程

你观察到的内存每15秒才上涨一次,是因为Python的垃圾回收是阈值触发的,不是对象销毁立刻就释放内存给操作系统,所以泄漏的资源会累积到一定阈值才会体现为进程内存上涨,和你测试的现象吻合。

现有测试代码的问题

你当前的两组测试代码都存在逻辑缺陷,会干扰测试结果:

  1. 测试用的文件1不管是否加terminate(),都没有调用资源回收接口,所以子进程退出后的残留资源一直没有释放,内存自然会持续上涨,加不加terminate()看不出差异
  2. 监控用的文件2中list_of_procs列表会无限追加所有历史pid,永远不会清理已经退出的进程,本身就存在内存泄漏,会干扰你对测试进程内存的判断

改进测试与排查方案

测试方案修正

  1. 调整文件1的逻辑,每次处理完子进程后主动回收资源:
import subprocess
import time

while True:
    # 不需要输出的话直接把输出重定向到空设备,避免缓冲区占用
    pinger = subprocess.Popen('ipconfig', stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    time.sleep(0.1)
    pinger.terminate()
    # 新增回收逻辑,等待子进程完全退出并释放资源
    pinger.wait()

如果不想阻塞等待子进程退出,可以用pinger.poll()定期检查子进程状态,返回非None即代表子进程已退出,资源可回收。
2. 调整监控代码的逻辑,定期清理已退出的pid,避免自身内存泄漏,同时可以新增父进程句柄数的监控,句柄泄漏通常比内存上涨更早表现出来。

异常排查方向

  • 先检查原业务代码中是否有未读取的PIPE管道,这种场景导致的内存泄漏速度通常更快
  • 检查是否有全局容器(列表、字典等)存储了所有Popen实例,导致对象无法被GC回收
  • 统计父进程的打开句柄数,如果句柄数和内存同步上涨,基本可以确定是子进程资源未回收导致的泄漏

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 13:36:04