Python中类似于PHP的memory_get_usage()函数的等效方法是什么?

24

我已经 找到了以下问题,但我想知道是否有一种更快捷、更简便的方式来估计Python解释器当前正在使用多少内存来运行我的脚本,而不依赖于外部库。

我之前用PHP,习惯使用memory_get_usage()以及memory_get_peak_usage()来做这件事情,我希望能够找到一个相应的函数。


可能是Python进程使用的总内存?的重复问题。 - Don Kirkby
6个回答

31

对于拥有/proc/self/status的Linux和其他系统,以下代码是一个简单的解决方案,我在我的项目中使用它:

def memory_usage():
    """Memory usage of the current process in kilobytes."""
    status = None
    result = {'peak': 0, 'rss': 0}
    try:
        # This will only work on systems with a /proc file system
        # (like Linux).
        status = open('/proc/self/status')
        for line in status:
            parts = line.split()
            key = parts[0][2:-1].lower()
            if key in result:
                result[key] = int(parts[1])
    finally:
        if status is not None:
            status.close()
    return result

它返回当前和峰值驻留内存大小(人们在谈论应用程序使用多少RAM时可能是这个意思)。很容易将其扩展为从/proc/self/status文件中获取其他信息。

对于好奇的人:完整的cat /proc/self/status输出如下:

% cat /proc/self/status
Name:   cat
State:  R (running)
Tgid:   4145
Pid:    4145
PPid:   4103
TracerPid:      0
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
FDSize: 32
Groups: 20 24 25 29 40 44 46 100 1000 
VmPeak:     3580 kB
VmSize:     3580 kB
VmLck:         0 kB
VmHWM:       472 kB
VmRSS:       472 kB
VmData:      160 kB
VmStk:        84 kB
VmExe:        44 kB
VmLib:      1496 kB
VmPTE:        16 kB
Threads:        1
SigQ:   0/16382
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000000000
SigCgt: 0000000000000000
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: ffffffffffffffff
Cpus_allowed:   03
Cpus_allowed_list:      0-1
Mems_allowed:   1
Mems_allowed_list:      0
voluntary_ctxt_switches:        0
nonvoluntary_ctxt_switches:     0

2
峰值/常驻内存是以KB还是字节为单位? - Shabbyrobe
1
好问题——它是以千字节为单位的,我已经将这个信息添加到原始答案中。 - Martin Geisler
非常感谢您的出色回答。顺便问一下,如果我生成一堆线程,为什么峰值会超过80mb(!!!),即使驻留内存保持相对较低,您有任何想法吗?此外,您是否知道如何在Win32上执行此操作的任何线索? - Shabbyrobe
不是挑刺,但它是正的千字节(1000)还是基比字节(1024)? - user4805123

18
你还可以使用标准库中的resource模块中的getrusage()函数。产生的对象有属性ru_maxrss,它给出了调用进程的总峰值内存使用量:
>>> import resource
>>> resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
2656

Python文档没有明确说明单位是什么,但Mac OS X man页面getrusage(2)的描述为千字节。

Linux man页面不够清晰,但它似乎等同于接受答案中描述的/proc/self/status信息(即千字节)。对于上述相同的进程,在Linux上运行,接受答案中列出的函数给出:

>>> memory_usage()                                    
{'peak': 6392, 'rss': 2656}

这种方法可能不像/proc/self/status解决方案那样易于使用,但它是标准库,因此(如果单位是标准的)应该是跨平台的,并可用于缺少/proc/(例如Mac OS X和其他Unix系统,也许是Windows)的系统。

此外,getrusage()函数还可以给出resource.RUSAGE_CHILDREN以获取子进程的使用情况,并且(在某些系统上)resource.RUSAGE_BOTH表示总体(自身和子进程)进程使用情况。

这将涵盖memory_get_usage()情况,但不包括峰值使用情况。我不确定resource模块中是否有其他函数可以提供峰值使用情况。


我的OSX(Lion)在我运行的进程上显示的是35819520,我相当确定它应该是35MB而不是35GB,所以看起来这应该是以字节为单位的。 :) - Kit Sunde
2
在我的Ubuntu 11.10机器上,我发现resource.getrusage()返回的值更接近于memory_usage()的峰值,而不是rss。您确定ru_maxrss是指当前内存使用情况而不是峰值内存使用情况吗? - Phani
1
@Phani 对我来说,它似乎也是使用高峰期。有关 ru_maxrss 的更多信息,请参见此答案:https://dev59.com/ymct5IYBdhLWcg3wgNcy#12050966 - Chris Conley
请注意,目前(Linux 3.10.7-2)提供当前驻留集大小的ru_idrss字段未被维护,因此它将返回0。此答案中有更多详细信息:https://dev59.com/s1rUa4cB1Zd3GeqPmKs8 - Emaad Ahmed Manzoor
1
Mac OS 绝对以字节为单位返回 RSS,Linux 以千字节为单位返回。 - Neil

11

接受答案的规则没错,但使用 psutil 可能更容易(且更便携)。它完成了相同的工作并且功能更加强大。

更新: muppy 也非常方便(且比guppy/heapy文档好得多)。


你的回答是我所接受的,但我并不是提问者,所以我能给你的最好评价就是点赞。 - CashCow
谢谢!我发现muppy在某些方面甚至更好,并且文档也非常好 - 如果您有内存泄漏问题,它值得一试。 - johndodo
具体细节请参见(几乎相同的)答案:https://dev59.com/8nNA5IYBdhLWcg3wfd8m#21632554 - 0 _

2

1

/proc/self/status有以下相关键:

  • VmPeak:虚拟内存峰值大小。
  • VmSize:虚拟内存大小。
  • VmHWM:峰值驻留集大小(“高水位标记”)。
  • VmRSS:驻留集大小。

因此,如果关注的是驻留内存,则可以使用以下代码来检索它:

def get_proc_status(keys = None):
    with open('/proc/self/status') as f:
        data = dict(map(str.strip, line.split(':', 1)) for line in f)

    return tuple(data[k] for k in keys) if keys else data

peak, current = get_proc_status(('VmHWM', 'VmRSS'))
print(peak, current)  # outputs: 14280 kB 13696 kB

这是一篇memory_profiler作者撰写的文章,讲解了getrusageru_maxrss并非总是实用的度量方式。同时需要注意的是,VmHWM可能不同于ru_maxrss(在某些情况下我看到ru_maxrss更大)。但在简单情况下它们是相同的。
import resource


def report():
    maxrss = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
    peak, current = get_proc_status(('VmHWM', 'VmRSS'))
    print(current, peak, maxrss)


report()

s = ' ' * 2 ** 28  # 256MiB
report()

s = None
report()

此外,这里有一篇非常易懂且信息量丰富的案例研究,由atop作者撰写, 解释了什么是内核、虚拟和常驻内存,以及它们之间的相互依存关系。

1

/proc/self/status 中的数据与 /proc/self/statm 中的数据相同。但是,它更容易解析,因为它只是由几个以空格分隔的统计数据组成的列表。我无法确定这两个文件是否始终存在。

/proc/[pid]/statm

提供有关内存使用情况的信息,以页面为单位测量。列如下:

  • size (1) 程序总大小(与/proc/[pid]/status中的VmSize相同)
  • resident (2) 常驻集大小(与/proc/[pid]/status中的VmRSS相同)
  • shared (3) 常驻共享页数(即由文件支持的页数)(与/proc/[pid]/status中的RssFile+RssShmem相同)
  • text (4) 文本(代码)
  • lib (5) 库(自Linux 2.6起未使用;始终为0)
  • data (6) 数据+堆栈
  • dt (7) 脏页(自Linux 2.6起未使用;始终为0)

以下是一个简单的示例:

from pathlib import Path
from resource import getpagesize

PAGESIZE = getpagesize()
PATH = Path('/proc/self/statm')


def get_resident_set_size() -> int:
    """Return the current resident set size in bytes."""
    # statm columns are: size resident shared text lib data dt
    statm = PATH.read_text()
    fields = statm.split()
    return int(fields[1]) * PAGESIZE


data = []
start_memory = get_resident_set_size()
for _ in range(10):
    data.append('X' * 100000)
    print(get_resident_set_size() - start_memory)

那会产生一个类似这样的列表:
0
0
368640
368640
368640
638976
638976
909312
909312
909312

你可以看到,在大约分配了3个100,000字节后,它会跳跃约300,000字节。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接