【问题标题】:Python memory footprint vs. heap sizePython 内存占用与堆大小
【发布时间】:2009-07-28 14:19:10
【问题描述】:

我在使用 python 脚本发出大型 solr 查询时遇到了一些内存问题。我正在使用 solrpy 库与 solr 服务器交互。该查询返回大约 80,000 条记录。发出查询后,通过顶部气球查看的 python 内存占用量立即增加到 ~190MB。

 PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND 
8225 root      16   0  193m 189m 3272 S  0.0 11.2   0:11.31 python
...

此时,通过 heapy 查看的堆配置文件如下所示:

Partition of a set of 163934 objects. Total size = 14157888 bytes.   
 Index  Count   %     Size   % Cumulative  % Kind (class / dict of class)
     0  80472  49  7401384  52   7401384  52 unicode
     1  44923  27  3315928  23  10717312  76 str
...

unicode 对象表示来自查询的记录的唯一标识符。需要注意的一点是,总堆大小只有 14MB,而 python 占用了 190MB 的物理内存。一旦存储查询结果的变量超出范围,堆配置文件就会正确反映垃圾回收:

Partition of a set of 83586 objects. Total size = 6437744 bytes.
 Index  Count   %     Size   % Cumulative  % Kind (class / dict of class)
     0  44928  54  3316108  52   3316108  52 str

但是,内存占用保持不变:

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
 8225 root      16   0  195m 192m 3432 S  0.0 11.3   0:13.46 python
...

为什么python的物理内存占用和python堆的大小差距这么大?

【问题讨论】:

  • 这种行为对于较小的查询是否也可以重现?
  • 物理内存占用量与查询大小成比例增加。

标签: python memory-leaks solr


【解决方案1】:

Python 从 C 堆中分配 Unicode 对象。因此,当您分配其中许多(连同其他 malloc 块),然后释放除最后一个之外的大部分,C malloc 不会将任何内存返回给操作系统,因为 C 堆只会在最后收缩(不在中间)。释放最后一个 Unicode 对象将释放 C 堆末尾的块,然后允许 malloc 将其全部返回给系统。

除了这些问题之外,Python 还维护一个释放的 unicode 对象池,以便更快地分配。因此,当最后一个 Unicode 对象被释放时,它并没有立即返回到 malloc,从而使所有其他页面都卡住了。

【讨论】:

  • 澄清一下,你是说heapy输出显示了python私有堆的内容。最上面的输出显示了通过 C API 分配给我的进程的总内存?如果是这种情况,您能否建议任何技术来确保一旦不再需要该内存的内容,python 会将原始内存释放回操作系统?
  • 正如罗伯托所说:使用最新的 Python 版本。如果问题没有消失,请向 bugs.python.org 报告错误,最好包括修复。
  • Linux 上的 Python 似乎没有将释放的内存释放回操作系统。更多信息:python.dzone.com/articles/diagnosing-memory-leaks-python
【解决方案2】:

CPython 实现仅异常释放分配的内存。这是一个广为人知的错误,但它并没有受到 CPython 开发人员的太多关注。推荐的解决方法是“分叉并死掉”消耗大量 RAM 的进程。

【讨论】:

  • 这是罗伯托提到的在 2.5 中修复的错误吗?还是已知此错误仍然存​​在?
  • hruske,你真的确定你在说什么吗?我没有遇到过这样的错误,我也没有从 python 错误跟踪器中了解它。
  • 根据我的经验,是的。例如,在一个守护进程中处理大量数据之后会给您留下大量的 malloc 内存,然后这些内存将在一段时间后被换出并且很可能不会被释放。但是,这可能不是因为 gc 错误,而是因为循环引用。如果有帮助,请尝试在这里查看lshift.net/blog/2008/11/14/tracing-python-memory-leaks
【解决方案3】:

你用的是什么版本的python?
我问是因为older version of CPython did not release the memory,这在 Python 2.5 中已修复。

【讨论】:

  • 当我看到您发布的链接时,我正在使用 CPython 2.4。不幸的是,当我在 2.6 中运行脚本时,我遇到了同样的行为。
  • 您确定您使用的是更新版本吗?我曾经遇到过类似的问题,最后我发现服务器还在使用旧的python版本。
  • 我在默认版本为 2.4 的 CentOS 机器上运行。我安装 2.6 只是为了测试这个问题。我正在运行我的脚本,显式调用 python2.6。
【解决方案4】:

我已经实现了 hruske 的“fork and die”的建议。我正在使用 os.fork() 在子进程中执行内存密集型代码部分,然后让子进程退出。父进程在子进程上执行 os.waitpid() 以便在给定时间只执行一个线程。

如果有人发现此解决方案存在任何缺陷,请加入。

【讨论】:

  • os.fork() 在多线程应用程序中可能会变得混乱。 fork “复制”整个进程内存,但只保持主线程运行。因此您在其他线程中输入的任何锁都可能使您的分叉应用死锁
猜你喜欢
  • 1970-01-01
  • 2019-12-12
  • 1970-01-01
  • 1970-01-01
  • 2012-06-20
  • 1970-01-01
  • 1970-01-01
  • 2013-08-26
  • 2018-10-04
相关资源
最近更新 更多