1. 这轮我掌握的核心结论
- 面试里这题的关键是排查思路,不是背工具参数。
- 标准流程:
- 先确认现象(RSS 是否持续上涨、是否可稳定复现)
- 再定位热点(
tracemalloc前后快照 +compare_to) - 再查引用链(
gc/objgraph) - 修复后回归验证(同样压测下内存是否趋稳)
- 如果
RSS在涨,但tracemalloc看不出热点,要考虑 C 扩展层分配,使用memray(常在 Linux/WSL 使用)。
2. 本次实操案例(可作为面试回答素材)
案例:全局容器导致内存持续增长
- 泄漏版文件:
code/memory_leak_lab/case1_global_list.py - 现象:每次调用都构造约 200KB payload 并
append到全局LEAK_BUCKET,导致对象一直被引用。 - 使用
tracemalloc分析后,增长热点定位到 payload 构造和 append 对应代码行。
修复方案
- 修复版文件:
code/memory_leak_lab/case1_fixed_bounded.py - 改法:把无界
list改为有界deque(maxlen=100)。 - 结果:
- 修复前:对象数量和内存随迭代线性增长
- 修复后:
count稳定在 100 左右,内存趋于稳定
这个"修复前后对比"是很好的面试证据链。
3. 关键概念速记
RSS 是什么
- RSS(Resident Set Size)= 进程当前驻留在物理内存中的大小。
- Windows 下常看
WorkingSet/WorkingSet64(可近似 RSS)。
compare_to(..., "lineno") 里的 lineno
lineno= line number(代码行号)- 表示按"文件 + 行号"聚合内存增长,适合快速定位。
compare_to 常见 key_type
filename:按文件聚合(粗粒度)lineno:按行聚合(常用)traceback:按完整调用栈聚合(细粒度)
4. objgraph.show_growth() 输出怎么看
输出列含义:
- 第 1 列:对象类型名(如
dict、list、function) - 第 2 列:当前该类型对象总数
- 第 3 列:相对上次
show_growth()的增量(+N)
注意:第一次调用常包含初始化噪音。实践里应该:
gc.collect()- 先
show_growth()建基线 - 跑业务循环
- 再
gc.collect()+show_growth(limit=10)看真实增量
5. 面试可直接说的回答模板
我会先确认问题是否可复现,并观察 RSS 是否持续上涨;然后用
tracemalloc在执行前后拍快照,通过compare_to定位增长最明显的代码行;如果对象没有释放,再用gc/objgraph查引用链,找出根引用(比如全局容器或无界缓存);修复后用同样压测回归,确认内存从线性增长变为稳定。若 RSS 增长与tracemalloc不匹配,我会怀疑 C 扩展层,并用memray进一步分析。
6. 我的真实表达策略(避免"硬编经历")
如果被问"你解决过吗",可以诚实表达:
- 线上未必遇到过典型事故,但做过完整演练
- 能给出复现、定位、修复、回归的完整闭环
- 有实际脚本和结果支撑,不是只会背概念
这样既真实,也能体现工程能力。