令人困惑的非对象Python内存泄漏

10
我有一个在uwsgi下运行的django web服务器,似乎存在内存泄漏问题。具体来说,进程的RSS会慢慢增长,直到最终需要重新启动服务器。我知道有其他类似的问题,但是到目前为止找到的所有解决方案/结论都不适用于这种情况(至少我没有找到)。到目前为止,我已经使用meliaeHeapypymplerobjgraph来检查Python堆栈,它们都报告了同样的结果:一个正常的堆栈占用约40MB的内存(预期),随着时间的推移变化很小(所期望的)。然而,这与进程RSS完全不一致,进程RSS可以愉快地增长到400MB+,而Python堆栈大小则没有反应。以下是一些示例输出以说明我的观点- Pympler输出比较Python堆栈/对象内存与进程RSS:
Memory snapshot:
                                        types |   # objects |   total size
============================================= | =========== | ============
                                         dict |       20868 |     19852512
                                          str |      118598 |     11735239
                                      unicode |       19038 |     10200248
                                        tuple |       58718 |      5032528
                                         type |        1903 |      1720312
                                         code |       13225 |      1587000
                                         list |       11393 |      1289704
                            datetime.datetime |        6953 |       333744
                                          int |       12615 |       302760
  <class 'django.utils.safestring.SafeUnicode |          18 |       258844
                                      weakref |        2908 |       255904
     <class 'django.db.models.base.ModelState |        3172 |       203008
                   builtin_function_or_method |        2612 |       188064
                       function (__wrapper__) |        1469 |       176280
                                         cell |        2997 |       167832
                            getset_descriptor |        2106 |       151632
                           wrapper_descriptor |        1831 |       146480
                                          set |         226 |       143056
                                      StgDict |         217 |       138328
---------------------------
Total object memory: 56189 kB
Total process usage:
 - Peak virtual memory size: 549016 kB
 - Virtual memory size: 549012 kB
 - Locked memory size: 0 kB
 - Peak resident set size: 258876 kB
 - Resident set size: 258868 kB
 - Size of data segment: 243124 kB
 - Size of stack segment: 324 kB
 - Size of code segment: 396 kB
 - Shared library code size: 57576 kB
 - Page table entries size: 1028 kB
---------------------------

堆输出显示了类似的事情。
Memory snapshot:
Partition of a set of 289509 objects. Total size = 44189136 bytes.
 Index  Count   %     Size   % Cumulative  % Kind (class / dict of class)
     0 128384  44 12557528  28  12557528  28 str
     1  61545  21  5238528  12  17796056  40 tuple
     2   5947   2  3455896   8  21251952  48 unicode
     3   3618   1  3033264   7  24285216  55 dict (no owner)
     4    990   0  2570448   6  26855664  61 dict of module
     5   2165   1  1951496   4  28807160  65 type
     6  16067   6  1928040   4  30735200  70 function
     7   2163   1  1764168   4  32499368  74 dict of type
     8  14290   5  1714800   4  34214168  77 types.CodeType
     9  10294   4  1542960   3  35757128  81 list
<1046 more rows. Type e.g. '_.more' to view.>
---------------------------
Total process usage:
 - Peak virtual memory size: 503132 kB
 - Virtual memory size: 503128 kB
 - Locked memory size: 0 kB
 - Peak resident set size: 208580 kB
 - Resident set size: 208576 kB
 - Size of data segment: 192668 kB
 - Size of stack segment: 324 kB
 - Size of code segment: 396 kB
 - Shared library code size: 57740 kB
 - Page table entries size: 940 kB
---------------------------

请注意,在这两种情况下,报告的堆大小为40-50MB,而进程RSS为200MB+。
我还使用了objgraph的get_leaking_objects()来尝试查看C扩展是否存在错误的引用计数,但是不可垃圾回收对象的数量随时间不会显着增长。
有人对如何进行调试有任何见解吗?此时,我认为有两种可能:
1. 我的C扩展在内部泄漏内存 2. uwsgi本身在泄漏内存(尽管我在网络上找不到其他证据)
值得一提的是,我在任何开发环境中都无法成功复制这一点(尽管我可能只是没有向它们投入足够的流量)。
我们确实使用了许多具有C扩展名的模块(simplejson、hiredis等),因此它们很可能是原因。
寻找追踪此问题的方法。

你的 settings.py 文件中 DEBUG = False,对吧? - monkut
是的,不过是个好问题 :) - fenn
你最终解决了这个问题吗?我们遇到了类似的问题,这些问题只在切换到uwsgi后才出现。 - Geekfish
你解决了吗?我遇到了类似的问题... - silviomoreto
1个回答

2
你使用的是哪个版本的Python?在Python 2.4中,Python内存分配器不会将内存归还给操作系统。
即使在较新的版本中,你仍然可能会遇到问题,这些问题可能与Python的内存分配器有关,它会保留已释放的简单类型列表;或者如果你在Linux上运行,则可能涉及到glibc的malloc实现从操作系统中分配内存的问题。请查看以下链接:http://effbot.org/pyfaq/why-doesnt-python-release-the-memory-when-i-delete-a-large-object.htmhttp://pushingtheweb.com/2010/06/python-and-tcmalloc/

在我们的情况下,使用Python 2.6(Ubuntu 10.04本机)。我会研究malloc问题,我还没有遇到过这个问题。 - fenn

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