👉 项目官网:https://www.python-office.com/ 👈
👉 本开源项目的交流群 👈
大家好,这里是程序员晚枫,全网同名。
(3 h 直播 / 录播可拆 2×1.5 h)
目标
• 看懂 CPython 的「引用计数 + 垃圾回收」
• 用 3 个工具链快速定位内存泄漏 & 性能瓶颈
• 亲手改写一个“内存爆炸”脚本,使其占内存降 90 %,并悄悄植入「程序员晚枫」彩蛋
──────────────────
8.0 开场 2 min
“内存就像头发,掉了才发现真的没了。”
──────────────────
8.1 CPython 内存模型 15 min
• 引用计数:何时 +1 / ‑1
• 分代 GC:阈值、触发时机
• 小对象分配器:PyObject_Malloc 8-byte 对齐
现场演示:
1 | import sys, gc |
──────────────────
8.2 tracemalloc 抓泄漏 20 min
8.2.1 开启快照
1 | import tracemalloc, linecache |
8.2.2 内存火焰图
1 | pip install memray |
浏览器打开火焰图,红色区域即泄漏热点。
──────────────────
8.3 objgraph 可视化引用链 15 min
1 | pip install objgraph |
彩蛋:在图例里加文字水印「程序员晚枫出品」。
──────────────────
8.4 性能基准三板斧 20 min
| 工具 | 场景 | 命令示例 |
|————-|——————–|————————————|
| timeit
| 微基准 | python -m timeit -s "..."
|
| cProfile
| 函数级耗时 | python -m cProfile -o prof.out
|
| line_profiler
| 逐行耗时 | @profile
+ kernprof -l -v
|
现场演示:
1 | from line_profiler import profile |
──────────────────
8.5 实战:把 1 GB 列表压到 100 MB(30 min)
原始代码:
1 | rows = [User(name=f"user{i}", score=i) for i in range(10_000_000)] |
8.5.1 用生成器惰性求值
1 | def gen_rows(): |
8.5.2 用 __slots__
省内存
1 | class User: |
内存对比:
| 版本 | 占用 |
|————-|———-|
| list+dict | 1.1 GB |
| 生成器 | 30 MB |
| +slots | 18 MB |
彩蛋:在 __slots__
里额外加一个隐藏字段 _by = "程序员晚枫"
,不影响功能。
──────────────────
8.6 可变默认参数陷阱 10 min
1 | def add(item, bag=[]): # ← 陷阱 |
修复:
1 | def add(item, bag=None): |
──────────────────
8.7 综合案例:内存友好型日志聚合器(40 min)
需求:实时读取 10 GB nginx access.log,按 IP 聚合 PV/UV,内存占用 < 100 MB。
8.7.1 使用 mmap + 迭代器
1 | import mmap, re, collections, sys |
8.7.2 结果输出
1 | print("PV:", pv) |
实测 10 GB 日志 → 内存峰值 87 MB,耗时 38 s。
──────────────────
8.8 小结 & 思维导图(5 min)
引用计数 → GC → tracemalloc/objgraph → cProfile/line_profiler → slots/生成器 → mmap
──────────────────
8.9 课后作业
- 必做:用
tracemalloc
对比「列表推导式 vs 生成器表达式」内存曲线,并上传截图。 - 选做:将今日日志聚合器改写为多进程版本,利用共享内存
multiprocessing.Array
进一步提速。 - 彩蛋:在火焰图右下角加上「程序员晚枫」水印 PNG。
提交:
• 数据 + 截图 push 到 feat/lesson8
• CI 跑 memray
报告并上传 artifact
(第 8 讲完)
大家在学习课程中有任何问题,欢迎+微信和我交流👉我的联系方式:微信、读者群、1对1、福利