如何在Python中使用psutil获取程序的最大内存使用量

3 投票
2 回答
6188 浏览
提问于 2025-04-18 00:26

我正在使用以下代码来获取程序的最大内存使用量。

    import os, subprocess , psutil
    def mem(cmd):
        try:
            with open('in.txt','r') as infile, open('out.txt', 'w') as outfile:
                p=psutil.Popen("./"+cmd,shell=False,stdin=infile,stdout = outfile)
            print p.memory_info()
        except Exception:
             print "Error"
    cmd=raw_input()
    mem(cmd)

问题是,有时候在程序刚开始运行时,内存使用的输出结果是(0,0),但之后就会显示正确的结果。我不知道为什么会这样。比如说,对于一些程序,比如C++的hello world程序,输出是pmem(rss=4096, vms=315392),这大约是0.3M(我觉得这个输出是以字节为单位的),但是在ideone.com上运行hello world程序时,输出却是大约3M。为什么会有这样的差异呢?

cmd是可执行文件的名称。

命令print subprocess.check_output(['ps', 'v', '-p', str(p.pid)])的输出结果是:

PID TTY STAT TIME MAJFL TRS DRS RSS %MEM COMMAND 16150 pts/16 Z+ 0:00 0 0 0 0 0.0 [a.out] <defunct>

这是我一个示例C++程序:

`int a[1000000];
int main()
{
    return 0;
}`

有时候返回pmem(rss=4096, vms=4313088),有时候返回pmem(rss=0,vms=0)

2 个回答

3

这里的问题是,psutils会快速从/proc文件系统中获取一个快照,具体可以在源代码中看到。

当你运行你的“你好,世界”示例时,有时候它会在Python有机会从/proc读取值之前就结束了。

一旦这个进程结束,它实际上就不再占用任何内存了。你可以通过使用strace来确认这一点。

open("/proc/13420/statm", O_RDONLY)     = 3
read(3, "0 0 0 0 0 0 0\n", 1024)        = 14

如果你把示例修改为使用像sleep这样的命令,你会发现psutils会稳定地返回内存使用情况。

#include <iostream>
#include <unistd.h>

int main()
{
  std::cout << "Hello World.. sleeping!";
  sleep(3);
}

你的Python脚本的输出...

a.out
meminfo(rss=286720, vms=12931072)

实现你想做的事情有一个简单的方法,就是使用/usr/bin/time命令,在大多数平台上,这个命令会给你启动的进程的平均总内存使用情况,或者像J.F Sebastian建议的那样使用valgrind……他在我研究和测试答案时也发了帖子;)

Hello World.. sleeping!0.00user 0.00system 0:03.00elapsed 0%CPU     
(0avgtext+0avgdata 1144maxresident)k
0inputs+0outputs (0major+348minor)pagefaults 0swaps
2

<defunct>表示这个子进程是一个僵尸进程(它已经结束了,但父进程还没有读取它的状态,通常是通过p.poll()p.wait()来读取)。看起来无论是psutil还是ps都显示这类进程的RSS(常驻内存集)为零。

结果取决于子进程是否会在调用p.memory_info()之前就退出。这是一个竞争的过程。如果你在C++程序的退出时添加了延迟,那么p.memory_info()可能会在子进程退出之前被调用,这样你就能得到非零的结果。

问题是我可以让任意程序进行评估。语言也不是固定的。难道没有优雅的解决方案吗?

你可能需要操作系统的支持,以便在子进程退出后仍然保存它的内存使用信息。或者你可以使用像valgrind这样的内存分析工具来运行程序并读取它的结果。要收集结果:

$ valgrind --tool=massif cmd arg1 arg2 

要查看结果,你可以使用ms_print

$ ms_print massif.out.* | less

或者使用图形界面的Massif-Visualizer

@mdadm建议了一种更简单的解决方案:time命令

from subprocess import Popen, PIPE

p = Popen(['time', '-f', '%M'] + args, stderr=PIPE)
ru_maxrss = int(p.communicate()[1])
print("Maximum rss %d KB" % ru_maxrss)

GNU time使用wait3()来填充资源使用信息(如果可用的话)。它可以在Python中调用:

import os
from subprocess import Popen

p = Popen(args)
ru = os.wait4(p.pid, 0)[2]
print("Maximum rss %d KB" % ru.ru_maxrss)

我比较了psutil.Process.memory_info(rss)返回的最大值与os.wait4返回的ru_maxrss值,以及valgrind --tool=massif报告的最大总内存:它们是相似的

另请参见:

撰写回答