requests库中的内存泄漏

1 投票
1 回答
8473 浏览
提问于 2025-04-18 12:38

我注意到在使用requests库获取一个pdf文件时,内存使用量大幅增加。这个文件本身大约是4MB,但分配给python进程的实际内存却增加了超过150MB!

有没有人知道造成这种情况的可能原因(或者解决办法)?

这是测试案例:

import requests,gc
def dump_mem():
  s = open("/proc/self/status").readlines()
  for line in s:
    if line.startswith("VmRSS"):
      return line

下面是我在解释器中得到的输出。

>>> gc.collect()
0
>>> dump_mem()
'VmRSS:\t   13772 kB\n'
>>> gc.collect()
0
>>> r = requests.get('http://www.ipd.uni-karlsruhe.de/~ovid/Seminare/DWSS05/Ausarbeitungen/Seminar-DWSS05')
>>> gc.collect()
5
>>> dump_mem()
'VmRSS:\t   20620 kB\n'
>>> r.headers['content-length']
'4089190'
>>> dump_mem()
'VmRSS:\t   20628 kB\n'
>>> gc.collect()
0
>>> c = r.content
>>> dump_mem()
'VmRSS:\t   20628 kB\n'
>>> gc.collect()
0
>>> t = r.text
>>> gc.collect()
8
>>> dump_mem()
'VmRSS:\t  182368 kB\n'

显然,我不应该把pdf文件当作文本来解码。但是,这种情况的原因到底是什么呢?

1 个回答

7

当内容类型中没有包含charset参数,并且响应不是text/类型时,系统会使用一个字符检测库来判断编码方式。

使用response.text时,你就触发了这个检测,加载了这个库,而它的模块里包含了一些比较大的数据表。

根据你安装的requests的具体版本,你会发现sys.modules['requests.packages.chardet']sys.modules['requests.packages.charade']现在已经存在了,还有大约36个子模块,这些在你使用r.text之前是没有的。

在检测过程中,会创建一些对象,这些对象会对你的PDF文档使用各种统计技术,因为检测没有足够的把握找到特定的编码方式。为了能把这些数据放进内存,Python会请求操作系统分配更多的内存。一旦检测过程完成,这些内存会被释放,但操作系统不会立即回收这些内存。这是为了防止内存的频繁申请和释放,因为进程可能会在循环中频繁请求和释放内存。

需要注意的是,你r.text的结果存储到了内存中,绑定到t。这是一个Unicode文本对象,在Python 2中,它占用的内存是字节字符串对象的2到4倍。你下载的这个内容作为字节字符串大约是4MB,但如果你使用的是UCS-4的Python版本,那么解码后的Unicode值会额外增加16MB的内存仅仅是为了这个解码后的值

撰写回答