Python 普通实例为什么会有额外的内存开销?
🧠 Python 的普通实例,背后背着一个你看不见的“万能属性背包”——dict。就是它把内存撑大了。
🎒 每个普通实例都自带一个字典¶
当你写这样一个类:
每个 Dog 实例内部,都会创建这样一个映射:
这个 dict 是一个完整的 Python 字典,即使里面只有一个键值对,它也需要维护:
-
哈希表结构(至少 8 个槽位,取决于实现)
-
键对象的指针、值对象的指针
-
版本标记、负载计数等元信息
用 sys.getsizeof 测一下就一目了然:
import sys
d = Dog('Buddy')
print(sys.getsizeof(d)) # 56 字节(实例本身)
print(sys.getsizeof(d.__dict__)) # 104 字节(字典,只有一个键!)
🗂️ 区区一个属性,字典开销居然是实例本身的近两倍。
🔒 对比:用 slots 关掉“背包”¶
如果预先知道属性有哪些,可以告诉 Python:“别给我背包了,给我几个固定格子就好”。
这时实例内部没有 dict,属性直接存储在实例内存的固定槽位里:
内存表现:
当你有几十万个对象时(比如游戏粒子、数据流记录),不带 slots 的内存浪费会急剧放大。
🧩 为什么字典那么“重”?¶
Python 字典是为动态查找、任意键、高适应性设计的通用容器,不是为你每个实例存两三个属性优化的。就算只有一个键,它也要:
-
分配一个哈希表数组(最小一般是 8 个槽)
-
每个槽存哈希值、键指针、值指针
-
维护负载因子、版本计数
这就像 用集装箱搬家只运了一本书——车(实例)不大,集装箱(dict)却死沉。