【发布时间】:2010-10-28 05:31:47
【问题描述】:
我已经found the following question,但我想知道是否有一种更快更脏的方法来估计python解释器当前为我的脚本使用了多少内存,而不依赖于外部库。
我来自 PHP,过去经常为此使用 memory_get_usage() 和 memory_get_peak_usage(),我希望能找到一个等价物。
【问题讨论】:
标签: python
我已经found the following question,但我想知道是否有一种更快更脏的方法来估计python解释器当前为我的脚本使用了多少内存,而不依赖于外部库。
我来自 PHP,过去经常为此使用 memory_get_usage() 和 memory_get_peak_usage(),我希望能找到一个等价物。
【问题讨论】:
标签: python
使用/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
【讨论】:
您也可以使用标准库模块resource 中的getrusage() 函数。结果对象具有属性ru_maxrss,它给出了调用进程的总峰值内存使用量:
>>> import resource
>>> resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
2656
Python docs 不清楚单位是什么,但Mac OS X man page 的getrusage(2) 将单位描述为千字节。
Linux 手册页不清楚,但它似乎等同于接受的答案中描述的/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 模块中的任何其他功能是否可以提供峰值使用率。
【讨论】:
35819520,我很确定是 35MB 而不是 35GB,所以它似乎是字节。 :)
ru_idrss 字段当前未维护(Linux 3.10.7-2),因此它将返回 0。此 answer 有更多详细信息。
【讨论】:
试试heapy
【讨论】:
/proc/self/status 具有以下相关键:
因此,如果关注的是常驻内存,我可以使用以下代码来检索它:
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
这是一个article by memory_profiler's author,它解释了getrusage 的ru_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()
此外,这里有一个非常易于理解但内容丰富的case study by atop authors,它解释了什么是内核、虚拟内存和常驻内存,以及它们是如何相互依赖的。
【讨论】:
/proc/self/status 中的同类数据也在/proc/self/statm 中。但是,它更容易解析,因为它只是几个statistics 的空格分隔列表。我无法判断这两个文件是否始终存在。
/proc/[pid]/statm
提供有关内存使用情况的信息,以页为单位。 列是:
- 大小 (1) 总程序大小 (与 /proc/[pid]/status 中的 VmSize 相同)
- resident (2) 驻留集大小 (与 /proc/[pid]/status 中的 VmRSS 相同)
- shared (3) 常驻共享页面的数量(即,由文件支持) (与 /proc/[pid]/status 中的 RssFile+RssShmem 相同)
- 文字(4)文字(代码)
- lib (5) 库(自 Linux 2.6 起未使用;始终为 0)
- 数据 (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 字节。
【讨论】: