sys.getsizeof()仅计算对象自身内存,不递归统计嵌套元素、引用共享、字符串驻留等细节;应改用pympler.asizeof()测真实占用,或tracemalloc追踪运行时增量分配。

sys.getsizeof() 只返回对象本身的开销,对列表、字典、嵌套结构这类复杂数据结构完全不适用——它不递归计算内部元素,也不处理引用共享、字符串驻留、小整数缓存等 Python 内存管理细节。想看真实内存占用,必须换方法。
为什么 sys.getsizeof() 对嵌套结构失效
它只测“容器壳子”,不进“里面”。比如一个含 1000 个字符串的字典:sys.getsizeof() 返回的只是 dict 结构体大小(约 216 字节),而实际内存可能高达几 MB。
- 字符串常量可能被驻留(
id("hello") == id("hello")),重复计数会虚高 - 小整数(-5 到 256)是全局单例,
sys.getsizeof(42)测的是引用,不是新分配 - 列表里存的是指针,
sys.getsizeof([x, y])不包含x和y占用的内存 - 自定义类实例还涉及
__dict__、__slots__、弱引用表等隐藏开销
用 pympler.asizeof() 替代 sys.getsizeof()
pympler 是目前最接近“开箱即用真实值”的方案。asizeof() 会递归遍历所有引用对象,跳过重复 ID(避免共享对象重复计数),并自动处理常见优化机制。
- 安装:
pip install pympler - 基本用法:
from pympler import asizeof; asizeof.asizeof(my_dict) - 支持
limit=参数防止无限递归(如循环引用) - 对
numpy数组、pandasDataFrame 也能识别并调用其原生nbytes或memory_usage(deep=True) - 注意:仍无法精确还原 C 扩展对象(如某些
cython类)的底层分配,但比sys.getsizeof()稳定得多
用 tracemalloc 追踪运行时增量分配
当你要定位“哪段代码让内存涨了 200MB”,而不是静态看某个变量——tracemalloc 是唯一可靠选择。它记录每次 malloc-级分配的调用栈,不受 Python 对象模型干扰。
- 启动要早:
import tracemalloc; tracemalloc.start(25)(25 表示保存最多 25 层调用帧) - 拍快照:
snap1 = tracemalloc.take_snapshot()→ 执行可疑代码 →snap2 = tracemalloc.take_snapshot() - 查差异:
snap2.compare_to(snap1, 'lineno')[:10]直接列出新增内存最多的 10 行 - 关键点:它测的是“新申请的内存块”,不是对象当前总驻留量;所以适合找泄漏,不适合算静态结构大小
别忽略 dtype 和底层存储格式
同一个逻辑数据,内存差 4 倍很常见——尤其是 numpy 和 pandas。例如:
-
np.array([1,2,3], dtype=np.int64)占 24 字节;dtype=np.int8只占 3 字节 -
pandas.Series默认用objectdtype 存数字?每个元素都是指针,比int64dtype 多出 8×N 字节 + 每个 int 对象 28 字节开销 -
pd.DataFrame的memory_usage(deep=True)必须加deep=True,否则字符串列只算指针,不算内容 - 用
.astype("category")或pd.SparseArray可以大幅压缩稀疏/重复数据
真实内存永远取决于你测的是“某一刻的驻留总量”“某段代码引发的新分配”还是“序列化后的磁盘体积”——三者数值毫无可比性。选错工具,结果就只是数字游戏。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











