Python 中数百万字符串列表的内存使用情况

Memory usage of a list of millions of strings in Python

所示,很难精确测量包含字符串的集合或列表所使用的内存。但这是一个很好的 estimation/upper 界限:

import os, psutil
process = psutil.Process(os.getpid())
a = process.memory_info().rss
L = [b"a%09i" % i for i in range(10_000_000)]
b = process.memory_info().rss
print(L[:10])  # [b'a000000000', b'a000000001', b'a000000002', b'a000000003', b'a000000004', b'a000000005', b'a000000006', b'a000000007', b'a000000008', b'a000000009']
print(b-a)
# 568762368 bytes

即100 MB 实际数据需要 569 MB。

已在 and 中找到改进此问题的解决方案(例如使用其他数据结构),所以这里我的问题不是“如何改进”,而是:

在标准字节串列表的情况下,我们如何精确地解释这个大小?

每个字节串有多少字节,每个(链接?)列表项最终获得 569 MB?

这将有助于理解 CPython 中列表和字节串的内部结构(平台:Windows 64 位)。

总结:

  • 列表对象 89 MB
  • 480 MB 用于字符串对象
  • => 总计 569 MB

sys.getsizeof(L) 会告诉您列表对象本身大约有 89 MB。这是几十个组织字节,每个字节串引用 8 个字节,以及高达 12.5% 的过度分配以允许高效插入。

sys.getsizeof(one_of_your_bytestrings) 会告诉你它们每个有 43 个字节。 That's:

  • 8 个字节用于参考计数器
  • 8 个字节用于指向类型的指针
  • 长度为 8 个字节(因为字节串的大小不固定)
  • 8 字节散列
  • 10 个字节用于您的实际字节串内容
  • 1 个字节用于终止 0 字节。

在内存中每 43 个字节存储对象会跨越内存字边界,这会更慢。所以它们实际上通常每 48 个字节存储一次。您可以使用 id(one_of_your_bytestrings) 获取要检查的地址。

(这里和那里存在一些差异,部分原因是发生了确切的内存分配,但 569 MB 是了解上述原因的预期值,它与您测量的值相符。)