了解Python进程的内存增长(VmRSS与gc.get_对象)

了解Python进程的内存增长(VmRSS与gc.get_对象),python,linux,memory,memory-leaks,garbage-collection,Python,Linux,Memory,Memory Leaks,Garbage Collection,在不讨论算法细节的情况下,让我们假设我的代码按顺序处理输入列表: 输入=[2,5,6,7,8,10,12,13,14,15,16,17,18,19,20,21] 对于输入中的i: 过程输入(i) 为简单起见,让我们考虑 PrimultPurrime是一个无状态的黑箱。 我知道这个站点充满了关于在Python代码中查找内存泄漏的问题,但这不是这个问题的内容。相反,我试图了解随着时间的推移我的代码的内存消耗情况,以及它是否会受到内存泄漏的影响 特别是,我试图理解两个不同的内存使用指标之间的差异:

在不讨论算法细节的情况下,让我们假设我的代码按顺序处理输入列表:

输入=[2,5,6,7,8,10,12,13,14,15,16,17,18,19,20,21]
对于输入中的i:
过程输入(i)

为简单起见,让我们考虑<代码> PrimultPurrime<代码>是一个无状态的黑箱。 我知道这个站点充满了关于在Python代码中查找内存泄漏的问题,但这不是这个问题的内容。相反,我试图了解随着时间的推移我的代码的内存消耗情况,以及它是否会受到内存泄漏的影响

特别是,我试图理解两个不同的内存使用指标之间的差异:

  • 分配的对象数()和
  • 实际使用的物理内存量()
为了研究这两个指标,我从上面扩展了原始代码,如下所示:

导入时间,gc def get_current_memory_usage(): 将open('/proc/self/status')作为f: memusage=f.read().split('VmRSS:')[1]。split('\n')[0][:-3] 返回int(memusage.strip())/(1024**2) 输入=[2,5,6,7,8,10,12,13,14,15,16,17,18,19,20,21] gc.collect() last\u object\u count=len(gc.get\u objects()) 对于输入中的i: 打印(f'\n处理输入{i}…') 过程输入(i) gc.collect() 时间。睡眠(1) 内存使用率=获取当前内存使用率() object\u count=len(gc.get\u objects()) 打印(f'Memory usage:{Memory\u usage:.2f}GiB') 打印(f'Object count:{Object\u count-last\u Object\u count:+}') 上次对象计数=对象计数 请注意,
process_input
是无状态的,即输入顺序无关紧要。因此,在运行
过程输入之前和之后,我们希望这两个指标大致相同,对吗?事实上,这就是我观察到的分配对象的数量。但是,内存消耗稳步增长:

现在我的核心问题是:这些观察结果是否表明内存泄漏?据我所知,Python中的内存泄漏将由分配对象的增长来表示,我们在这里没有观察到这一点。另一方面,为什么内存消耗会稳步增长

为了进一步调查,我还进行了第二次测试。对于这个测试,我使用一个固定的输入反复调用
process\u input(I)
(每次五次),并记录两次迭代之间的内存消耗:

  • 对于
    i=12
    ,内存消耗保持在10.91 GiB不变
  • 对于
    i=14
    ,内存消耗保持恒定在7.00 GiB
我认为,这些观察结果使得内存泄漏的可能性更大,对吗?但是,考虑到
过程输入
是无状态的,有什么可能的解释来解释为什么内存消耗不在两次迭代之间


该系统总共有32千兆内存,运行的是Ubuntu 20.04。Python版本是3.6.10。
process\u input
函数使用多个第三方库。

一般来说,RSS不是一个特别好的指示器,因为它是“驻留”设置大小,即使是一个相当小猪的进程,在提交内存方面,也可以有一个适度的RSS,因为内存可以交换。您可以查看/proc/self/smap并将可写区域的大小相加,以获得更好的基准

另一方面,如果确实有增长,并且您想了解原因,则需要查看实际的动态分配内存。我的建议是使用

要做到这一点,只需将1秒钟的睡眠时间延长一点,在调用睡眠之前打印,并在几次睡眠期间使用另一个会话中的gcore收集一个活动核心

假设你收集了输入为14和21时的内核。使用chap查看每个核心,例如,使用以下命令:

count used
这将为您提供已请求但未发布的分配的良好视图。如果后期核心的数字要大得多,则可能存在某种增长问题。如果这些数字确实相差很大,请使用

summarize used
如果有生长,则可能存在泄漏(与某些容器只是膨胀相反)。要检查这一点,您可以尝试以下命令

count leaked
show leaked 
从那里,您可能应该查看文档,这取决于您发现的内容

o如果已使用的分配不是问题,可以尝试以下操作,查看已释放的分配的内存,但这些分配是较大内存区域的一部分,无法返回给操作系统,因为这些区域的部分仍在使用中:

count free
summarize free
如果“已使用”分配或“免费”分配都不是问题,您可以尝试:

summarize writable
这是所有可写内存的高级视图。例如,您可以看到堆栈使用情况