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堆的大小有这么大的差距?

4

4 回答 4

5

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

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

于 2009-07-28T14:36:47.670 回答
2

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

于 2009-07-28T17:06:03.303 回答
1

你用的是什么版本的python?
我问是因为旧版本的 CPython 没有释放内存,这在 Python 2.5 中已修复。

于 2009-07-28T14:41:07.883 回答
0

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

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

于 2009-07-28T19:35:36.467 回答