
__sizeof__ 返回的只是对象本身的内存,不包括引用对象
直接调用 __sizeof__ 得到的数值,往往远小于你“感觉”该对象占的内存。比如一个包含 1000 个整数的列表,list.__sizeof__() 只返回列表结构体本身的开销(约 80 字节),不包含那 1000 个 int 对象各自占用的空间——它们是独立分配在堆上的,被列表引用,但不算在列表自身的 __sizeof__ 里。
这意味着:如果你只看 obj.__sizeof__(),对大多数复合对象(list、dict、自定义类实例)来说,结果基本没实用价值。
- 内置容器类型(
list、tuple、dict)的__sizeof__通常只覆盖其内部指针数组和元数据,不递归计算元素 - 自定义类实例的
__sizeof__默认继承自object,返回的是实例字典(__dict__)和对象头的大小,不含属性值本身(如果值是小整数、短字符串等可能被缓存或内联,情况更复杂) -
sys.getsizeof()是更安全的起点,它会尝试调用对象的__sizeof__并加上常见引用对象的估算(如dict的键值对),但仍非完全递归
想真正算清总内存,必须手动递归 + 排重
Python 没有内置的“深内存大小”函数,因为对象图可能存在循环引用、共享子对象(比如多个对象引用同一个 str 或 tuple),盲目递归会重复计数或栈溢出。
可靠的做法是用 sys.getsizeof() 配合 gc.get_referents() 手动遍历,并用 id() 去重:
Python 3.14.2是Python编程语言在2025年12月5日发布的稳定版本,属于3.14系列的第二个维护更新。该版本包含了18项修复,重点解决了多进程、数据类及正则表达式等模块的回归问题,并修复了CVE-2025-12084等安全漏洞。此版本标志着自由线程模式(移除GIL)正式获得官方支持,是Python发展的重要里程碑。
import sys
import gc
<p>def deep_sizeof(obj, seen=None):
if seen is None:
seen = set()
obj_id = id(obj)
if obj_id in seen:
return 0
seen.add(obj_id)
size = sys.getsizeof(obj)</p><h1>获取直接引用的对象(跳过字符串、小整数等不可变常量,避免误入全局池)</h1><pre class="brush:python;toolbar:false;">for referent in gc.get_referents(obj):
if not isinstance(referent, (str, bytes, int, float, type(None), bool)):
size += deep_sizeof(referent, seen)
return size
- 这个函数不是万能的:它跳过了
str、int等常见不可变类型,因为它们可能被大量共享,计入会导致严重高估 - 对含
__slots__的类,__dict__不存在,sys.getsizeof()行为不同,需单独处理 - 某些 C 扩展对象(如
numpy.ndarray)可能不响应gc.get_referents(),得查其文档找专用方法(如.nbytes)
自定义类要精确控制,得重写 __sizeof__ 并明确语义
如果你真需要让自己的类支持 __sizeof__,别只返回 super().__sizeof__()。必须想清楚:你希望它反映什么?是“本实例结构体大小”,还是“本实例及其直接持有数据的总大小”?两者语义完全不同。
例如一个缓存类:
class Cache:
def __init__(self):
self._data = {}
self._meta = []
<pre class="brush:python;toolbar:false;">def __sizeof__(self):
# 只算自身结构 + dict/tuple 的容器开销(不递归内容)
return super().__sizeof__() + sys.getsizeof(self._data) + sys.getsizeof(self._meta)
- 这样写至少比默认值有用,但它依然不包含
self._data里 key/value 占的内存 - 如果业务真依赖“总内存上限”,建议暴露一个
estimated_memory_usage()方法,内部调用上面提到的deep_sizeof并注明“含估算,不含共享对象” - 切记:重写
__sizeof__不会影响sys.getsizeof()的行为——后者会优先调用你的实现,但不会自动递归
实际调试时,优先用 memory_profiler 而非手算
手工实现 deep_sizeof 容易出错,且难以覆盖所有边缘情况(比如 weakref、closure、frame 对象)。真实场景下,更推荐用 memory_profiler 包做行级或函数级监控:
pip install memory-profiler # 然后在脚本里加装饰器或行魔术
-
@profile装饰器能显示每行内存增量,比静态大小更有诊断价值 -
memory_usage(-1)可抓取当前进程整体内存,适合观察泄漏趋势 - 它底层也用
gc.get_objects()和sys.getsizeof(),但做了大量适配和过滤,比自己写的鲁棒得多
真正难的不是算单个对象,而是理解哪些对象不该被计入、哪些被反复引用、哪些生命周期异常——这些靠数字本身说不清,得结合引用链和业务逻辑判断。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










