python如何查看有哪些变量以及各个变量占用的内存大小
在 Python 中查看有哪些变量,优先用 locals()、globals() 或 vars();查看变量占用的内存大小,快速方法是对变量调用 sys.getsizeof(),如果要统计列表、字典、自定义对象连同其引用对象的整体大小,应使用 pympler.asizeof 这类递归统计工具;如果要定位程序运行过程中哪里持续分配内存,应使用标准库 tracemalloc。
先判断你要看的是哪个作用域的变量
可执行做法:在当前函数内部看变量,用 locals();在脚本或模块级别看全局变量,用 globals();想查看某个对象自身保存的属性,用 vars(obj)。

x = 100
name = "python"
print(locals()) # 当前作用域变量
print(globals()) # 当前模块全局变量
print(vars()) # 不传参数时,通常等价于 locals()
判断标准:如果你只想知道“当前这一行代码能访问哪些变量”,用 locals();如果你想知道模块里定义了哪些变量、函数、类,用 globals();如果你想看对象有哪些实例属性,用 vars(对象)。
场景差异:在普通 .py 文件的模块顶层,locals() 和 globals() 通常指向同一批变量;在函数内部,locals() 只显示函数局部变量;在类实例上,vars(obj) 读取的是对象的 __dict__。
注意事项:不要依赖修改 locals() 返回的字典来改变局部变量值,这在函数内部并不可靠。实际排查时还应过滤掉 __name__、__file__、__builtins__ 等系统变量,否则输出会很长。
查看单个变量占用多少内存
可执行做法:导入 sys,对目标变量调用 sys.getsizeof()。返回值单位是字节。
import sys
x = 1000
text = "python如何查看有哪些变量以及各个变量占用的内存大小"
items = [1, 2, 3, 4, 5]
print(sys.getsizeof(x))
print(sys.getsizeof(text))
print(sys.getsizeof(items))
判断标准:如果你只是比较几个简单变量,比如整数、字符串、短列表的表层大小,sys.getsizeof() 足够快;如果变量是嵌套列表、字典、对象树,它只能给出“浅层大小”,不能代表整体内存。
场景差异:字符串的内容通常属于字符串对象自身的一部分,sys.getsizeof("abc") 的结果可以反映字符串对象大小;列表、元组、字典保存的是对其他对象的引用,sys.getsizeof([1, 2, 3]) 主要统计容器本身,不会完整累计里面每个整数对象的大小。
注意事项:不同 Python 版本、不同操作系统、不同解释器实现下,同一对象的大小可能不同。线上性能分析不要只看一个变量的 getsizeof,还要结合对象数量、生命周期和是否被长期引用。
一次列出所有变量及其浅层内存大小
可执行做法:把 locals() 或 globals() 传给一个函数,过滤系统变量后排序输出。
import sys
def show_vars_size(namespace, top=20):
rows = []
for name, value in namespace.items():
if name.startswith("__"):
continue
try:
size = sys.getsizeof(value)
except TypeError:
continue
rows.append((name, type(value).__name__, size))
rows.sort(key=lambda x: x[2], reverse=True)
for name, type_name, size in rows[:top]:
print(f"{name:30s} {type_name:20s} {size:10d} bytes")
a = [1, 2, 3]
b = {"name": "python", "count": 100}
c = "memory"
show_vars_size(globals())
判断标准:输出结果中,排在前面的变量只是“对象外壳更大”,不一定代表它们真实占用了最多内存。比如一个字典变量浅层只有几百字节,但它引用的大量字符串、列表、对象可能才是真正的内存来源。
场景差异:调试脚本时用 show_vars_size(globals()) 很方便;调试函数内部时应在函数里调用 show_vars_size(locals());在 Jupyter Notebook 中,全局命名空间常混有 In、Out、_ 等交互变量,需要额外过滤。
注意事项:打印变量值本身可能触发很长的 repr,甚至让日志膨胀。排查内存时优先输出变量名、类型、大小,不要默认打印完整值。
统计嵌套对象的真实占用
可执行做法:安装并使用 pympler 的 asizeof,它会递归统计对象引用到的内容,更适合列表、字典、自定义类实例等复杂结构。
pip install pympler
from pympler import asizeof
data = {
"users": [{"id": i, "name": f"user_{i}"} for i in range(10000)],
"tags": ["python", "memory", "debug"]
}
print(asizeof.asizeof(data), "bytes")
判断标准:如果 sys.getsizeof(data) 很小,但程序内存明显变大,说明你需要看递归大小;如果对象中包含大量嵌套容器、重复引用、自定义实例,优先用 asizeof.asizeof()。
场景差异:配置字典、缓存字典、接口返回的大型 JSON、Pandas 或 NumPy 外围对象,都更接近“对象图”问题,而不是单个变量问题。普通业务对象用 pympler 很直接;涉及 NumPy、PyTorch、TensorFlow 等库时,还要查看它们各自的数据缓冲区或显存统计接口。
注意事项:递归统计是近似值,运行也比 sys.getsizeof() 慢。不要在高频线上请求中反复执行完整递归统计,建议只在调试脚本、诊断命令、测试环境或采样任务中使用。
定位运行过程中内存增长的位置
可执行做法:使用标准库 tracemalloc。它不是按变量名统计,而是按文件名、行号、调用栈统计 Python 内存分配,更适合回答“哪一行代码让内存变大”。
import tracemalloc
tracemalloc.start()
# 运行你怀疑占内存的代码
data = []
for i in range(100000):
data.append({"id": i, "name": f"user_{i}"})
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics("lineno")
for stat in top_stats[:10]:
print(stat)
判断标准:如果你看到某个文件某一行的 size 和 count 持续增长,就说明那里在大量分配对象。要判断是否泄漏,可以在操作前后各拍一次快照,再用 compare_to() 比较差异。
import tracemalloc
tracemalloc.start()
before = tracemalloc.take_snapshot()
# 执行一次业务操作
result = [{"x": i} for i in range(50000)]
after = tracemalloc.take_snapshot()
diff = after.compare_to(before, "lineno")
for stat in diff[:10]:
print(stat)
场景差异:查看“当前有哪些变量”用 locals() 或 globals();查看“某个变量多大”用 getsizeof 或 pympler;查看“程序为什么越跑越占内存”用 tracemalloc。这三个问题不要混在一起,否则容易误判。
注意事项:tracemalloc 只追踪启动之后的 Python 内存分配,最好在程序入口尽早调用 tracemalloc.start()。它也不等于操作系统看到的进程 RSS,某些 C 扩展、原生库、显存分配可能无法完整反映。
Notebook 和交互式环境中的查看方法
可执行做法:在 Jupyter 或 IPython 中,可以先用 %who、%whos 快速看变量名和类型,再用 Python 代码精确统计重点变量。
# 在 Notebook 单元格中执行
%who
%whos
import sys
for name, value in globals().items():
if name.startswith("_"):
continue
if name in {"In", "Out", "get_ipython", "exit", "quit"}:
continue
print(name, type(value).__name__, sys.getsizeof(value))
判断标准:如果只是想确认当前 Notebook 里有哪些变量,%whos 更直观;如果要写成可复用脚本或保存诊断结果,使用 globals() 加过滤逻辑更稳定。
场景差异:Notebook 容易因为反复运行单元格保留旧变量,导致内存越来越大;普通脚本通常随进程结束释放内存。因此 Notebook 中排查内存时,除了看变量大小,还要关注已经不用但仍存在于全局命名空间的变量。
注意事项:删除大变量后可以执行 del var_name,必要时再调用 gc.collect() 触发垃圾回收。但如果对象仍被其他变量、缓存、闭包或 Notebook 输出历史引用,单独 del 一个名字并不一定释放内存。
如何判断是不是内存泄漏
可执行做法:重复执行同一段业务逻辑,观察变量数量、对象数量、快照差异是否持续增长。可以结合 gc、tracemalloc 和对象引用分析工具。
import gc
gc.collect()
print(len(gc.get_objects()))
判断标准:一次操作后内存上升不一定是泄漏,可能是缓存、解释器内存池或库初始化;多次执行同一操作后,同类对象数量和快照差异持续增加,且无法通过释放引用恢复,才更像泄漏。
场景差异:Web 服务中常见问题是全局列表、全局字典、LRU 缓存、单例对象不断保存请求数据;数据处理脚本中常见问题是循环里把中间结果追加到列表却没有清理;GUI、爬虫、异步任务中常见问题是回调、闭包、任务队列保留了对象引用。
注意事项:Python 的垃圾回收可以处理多数循环引用,但对象被全局容器、缓存、日志上下文、异常栈、闭包变量引用时,垃圾回收器不会释放它们。解决问题的关键不是强行调用 gc.collect(),而是找到是谁还在引用对象。
推荐的排查流程
可执行做法:先列变量,再看浅层大小,再对可疑变量做递归统计,最后用快照定位增长代码。这个顺序成本低,误判少。
- 用
locals()或globals()确认当前有哪些变量。 - 用
sys.getsizeof()对所有变量做快速排序。 - 对列表、字典、自定义对象使用
pympler.asizeof.asizeof()看递归大小。 - 如果内存随时间增长,用
tracemalloc对比两次快照。 - 确认不再需要的对象后,删除引用、清理缓存或调整数据结构。
判断标准:如果只是在开发时临时查看变量,内置函数足够;如果要解释“为什么这个对象这么大”,用递归统计;如果要解释“为什么服务运行一天后变慢”,必须看时间维度和分配位置。
场景差异:小脚本更关注变量名和单个对象大小;长期运行的服务更关注增长趋势;数据科学环境更关注大数组、大 DataFrame 和 Notebook 历史变量;生产系统更适合采样、日志化和压测环境复现。
注意事项:不要把“变量名”理解为“对象本身”。Python 中多个变量名可以引用同一个对象,删除其中一个变量名不一定释放对象;只有当对象没有任何强引用时,它才可能被回收。
常见问题
Python 怎么查看当前所有变量?
在当前位置执行 locals() 可以查看当前作用域变量;在模块级别执行 globals() 可以查看全局变量;在对象上执行 vars(obj) 可以查看对象实例属性。
为什么 sys.getsizeof(list) 看起来很小?
因为 sys.getsizeof() 统计的是列表对象本身的浅层大小,不会完整递归累计列表里每个元素的大小。嵌套列表、字典、自定义对象应使用 pympler.asizeof 之类工具。
怎么找出最占内存的变量?
先遍历 globals() 或 locals(),用 sys.getsizeof() 排序找出浅层最大的变量;再对可疑的大容器使用 asizeof.asizeof() 统计递归大小。
Jupyter Notebook 变量删了为什么内存没降?
可能是对象仍被其他变量、输出历史、缓存或闭包引用,也可能是 Python 或底层库没有立刻把内存归还给操作系统。可以检查 globals()、清理 Out,并确认没有其他引用。
tracemalloc 能直接告诉我哪个变量占内存最大吗?
不能。tracemalloc 主要按代码位置和调用栈统计内存分配,不按变量名统计。它适合定位哪行代码分配了大量内存,再结合变量检查工具继续分析。
参考文献
原创文章,作者:钱林雅,如若转载,请注明出处:https://www.wanglitou.cn/article_135558.html
微信扫一扫