guppy报告的内存使用与ps命令不同

9 投票
3 回答
4731 浏览
提问于 2025-04-15 18:02

我在分析我的Twisted服务器的性能。没想到它用的内存比我预期的要多,而且内存使用量随着时间的推移还在增加。

 ps -o pid,rss,vsz,sz,size,command
  PID   RSS    VSZ    SZ    SZ COMMAND
 7697 70856 102176 25544 88320 twistd -y broadcast.tac

从这里可以看到,它消耗了102176 KB,也就是99.78125 MB。我使用了Guppy这个工具,通过Twisted的manhole来查看内存使用情况。

>>> hp.heap()
Partition of a set of 120537 objects. Total size = 10096636 bytes.
 Index  Count   %     Size   % Cumulative  % Kind (class / dict of class)
     0  61145  51  5309736  53   5309736  53 str
     1  27139  23  1031596  10   6341332  63 tuple
     2   2138   2   541328   5   6882660  68 dict (no owner)
     3   7190   6   488920   5   7371580  73 types.CodeType
     4    325   0   436264   4   7807844  77 dict of module
     5   7272   6   407232   4   8215076  81 function
     6    574   0   305776   3   8520852  84 dict of class
     7    605   1   263432   3   8784284  87 type
     8    602   0   237200   2   9021484  89 dict of type
     9    303   0   157560   2   9179044  91 dict of zope.interface.interface.Method
<384 more rows. Type e.g. '_.more' to view.>

嗯……看起来有些问题。Guppy显示的总内存使用量是10096636字节,也就是9859.996 KB或者9.628 MB

这之间差距可真大。这个奇怪的结果是怎么回事呢?我哪里做错了?

更新: 我昨晚写了一个监控脚本。它记录了内存使用情况和在线用户的数量。因为这是一个广播服务器,所以你可以看到有多少个广播和总听众。这里是我用matplotlib生成的图表。

alt text

有些地方很奇怪。有时候,ps命令显示的内存使用量非常低,像这样:

2010-01-15 00:46:05,139 INFO 4 4 17904 36732 9183 25944
2010-01-15 00:47:03,967 INFO 4 4 17916 36732 9183 25944
2010-01-15 00:48:04,373 INFO 4 4 17916 36732 9183 25944
2010-01-15 00:49:04,379 INFO 4 4 17916 36732 9183 25944
2010-01-15 00:50:02,989 INFO 4 4 3700 5256 1314 2260

那么,内存使用量这么低的原因是什么呢?更奇怪的是,即使没有在线广播和听众,内存使用量依然很高。

3 个回答

0

这不是一个完整的答案,但根据你的情况,我建议你在使用 ps 或 top 之前,手动运行 gc.collect()。guppy 可以显示已分配的内存堆,但它并不会主动释放那些不再使用的对象。

3

正如上面提到的,RSS大小是你最关心的内容。“虚拟”大小包括了映射的库,这些你可能不想算在内。

我已经有一段时间没用heapy了,但我很确定它打印的统计数据不包括heapy本身增加的开销。这部分开销可能相当大(我见过一个100MB的RSS进程又增加了十几MB,具体可以参考http://www.pkgcore.org/trac/pkgcore/doc/dev-notes/heapy.rst)。

不过在你的情况下,我怀疑问题出在你使用的某个C库,它可能存在内存泄漏或者以heapy无法追踪的方式使用内存。heapy能识别直接由Python对象使用的内存,但如果这些对象包裹了单独分配的C对象,heapy通常就无法识别这些内存了。你可能需要为你的绑定添加heapy支持(但如果你无法控制使用的绑定,那就会很麻烦,即使你能控制绑定,也可能因为你包裹的内容而无法做到这一点)。

如果在C层面上有内存泄漏,heapy也会失去对那部分内存的追踪(RSS大小会增加,但heapy报告的大小保持不变)。Valgrind可能是你追踪这些问题的最佳选择,就像在其他C应用程序中一样。

最后,内存碎片化通常会导致你的内存使用量(在top命令中看到的)上升但不下降(变化不大)。对于守护进程来说,这通常不是大问题,因为进程会重用这些内存,只是没有释放回操作系统,所以在top中的值不会下降。如果内存使用量(在top中看到的)随着用户(连接)数量的增加而大致线性上升,不会下降,但也不会一直增长到新的用户最大值,那么碎片化可能就是罪魁祸首。

6

这可能是因为交换/内存保留,根据ps的定义:

RSS: resident set size, the non-swapped physical memory
     that a task has used (in kiloBytes).

VSZ: virtual memory usage of entire process.
     vm_lib + vm_exe + vm_data + vm_stack

这可能有点让人困惑,因为可以看到4种不同的大小指标:

# ps -eo pid,vsz,rss,sz,size,cmd|egrep python

PID    VSZ   RSS   SZ    SZ    CMD
23801  4920  2896  1230  1100  python

虚拟大小包括了进程保留但未使用的内存、加载的所有共享库的大小、被交换出去的页面,以及你进程已经释放的块,所以它可能比Python中所有活跃对象的大小要大得多。

一些额外的工具可以帮助你调查内存性能:

这是一个很好的指南,教你如何使用pdb和objgraph来追踪Python中的内存泄漏:

http://www.lshift.net/blog/2008/11/14/tracing-python-memory-leaks

撰写回答