Python内存占用与堆大小

10 投票
4 回答
5983 浏览
提问于 2025-04-15 13:13

我在用一个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的物理内存使用量和堆的大小之间会有这么大的差距呢?

4 个回答

1

你在用哪个版本的Python?
我问这个是因为早期的CPython版本没有释放内存,这个问题在Python 2.5中得到了修复。

3

CPython这个实现只在特殊情况下才会释放分配的内存。这是一个大家都知道的问题,但CPython的开发者对此并没有太多关注。推荐的解决办法是“分叉并结束”占用大量内存的进程。

6

Python 从 C 的内存堆中分配 Unicode 对象。所以当你分配了很多这样的对象(还有其他的内存块),然后释放掉大部分,只留下最后一个时,C 的内存分配不会把内存还给操作系统,因为 C 的内存堆只会在最后缩小,而不会在中间缩小。只有释放掉最后一个 Unicode 对象,才能释放 C 内存堆末尾的那块内存,这样 malloc 才能把所有的内存还给系统。

除了这些问题,Python 还维护了一池已经释放的 Unicode 对象,以便更快地分配内存。所以当最后一个 Unicode 对象被释放时,它并不会立刻还给 malloc,这样就会导致其他的内存页面被卡住。

撰写回答