Day 053 — 内存管理与垃圾回收图解
1. Python 内存管理架构
graph TD
A[Python 应用程序] --> B[内存分配器]
B --> C[pymalloc]
B --> D[操作系统]
C --> E[对象分配]
C --> F[内存池]
E --> G[引用计数]
E --> H[垃圾回收器]
G --> I[自动回收]
H --> J[标记-清除]
H --> K[分代回收]
graph TD
A[Python 应用程序] --> B[内存分配器]
B --> C[pymalloc]
B --> D[操作系统]
C --> E[对象分配]
C --> F[内存池]
E --> G[引用计数]
E --> H[垃圾回收器]
G --> I[自动回收]
H --> J[标记-清除]
H --> K[分代回收]
2. 引用计数机制
sequenceDiagram
participant Var as 变量
participant Obj as 对象
Var->>Obj: 创建对象 (refcount=1)
Var->>Obj: 赋值给另一个变量 (refcount=2)
Var->>Obj: 放入列表 (refcount=3)
Note over Obj: 对象仍然存活
Var->>Obj: del 变量 (refcount=2)
Var->>Obj: 从列表移除 (refcount=1)
Var->>Obj: del 最后一个引用 (refcount=0)
Note over Obj: 对象被立即回收
sequenceDiagram
participant Var as 变量
participant Obj as 对象
Var->>Obj: 创建对象 (refcount=1)
Var->>Obj: 赋值给另一个变量 (refcount=2)
Var->>Obj: 放入列表 (refcount=3)
Note over Obj: 对象仍然存活
Var->>Obj: del 变量 (refcount=2)
Var->>Obj: 从列表移除 (refcount=1)
Var->>Obj: del 最后一个引用 (refcount=0)
Note over Obj: 对象被立即回收
3. 循环引用问题
graph LR
A[对象A] -->|强引用| B[对象B]
B -->|强引用| A
style A fill:#ff6b6b,color:#fff
style B fill:#ff6b6b,color:#fff
graph LR
A[对象A] -->|强引用| B[对象B]
B -->|强引用| A
style A fill:#ff6b6b,color:#fff
style B fill:#ff6b6b,color:#fff
4. 分代回收原理
graph TD
subgraph 第 0 代
A0[新对象] --> B0[存活对象]
end
subgraph 第 1 代
A1[从 0 代存活] --> B1[存活对象]
end
subgraph 第 2 代
A2[从 1 代存活] --> B2[长期存活]
end
B0 -->|存活| A1
B1 -->|存活| A2
graph TD
subgraph 第 0 代
A0[新对象] --> B0[存活对象]
end
subgraph 第 1 代
A1[从 0 代存活] --> B1[存活对象]
end
subgraph 第 2 代
A2[从 1 代存活] --> B2[长期存活]
end
B0 -->|存活| A1
B1 -->|存活| A2
5. 标记-清除算法
graph TD
A[开始 GC] --> B[标记阶段]
B --> C[从根对象遍历]
C --> D[标记可达对象]
D --> E[清除阶段]
E --> F[回收未标记对象]
E --> G[保留标记对象]
graph TD
A[开始 GC] --> B[标记阶段]
B --> C[从根对象遍历]
C --> D[标记可达对象]
D --> E[清除阶段]
E --> F[回收未标记对象]
E --> G[保留标记对象]
6. gc 模块 API
graph TD
subgraph gc 模块
A[gc.collect] --> B[手动触发回收]
C[gc.get_objects] --> D[获取所有对象]
E[gc.get_referrers] --> F[获取引用者]
G[gc.get_referents] --> H[获取被引用对象]
I[gc.get_threshold] --> J[获取回收阈值]
K[gc.get_count] --> L[获取各代对象数]
end
graph TD
subgraph gc 模块
A[gc.collect] --> B[手动触发回收]
C[gc.get_objects] --> D[获取所有对象]
E[gc.get_referrers] --> F[获取引用者]
G[gc.get_referents] --> H[获取被引用对象]
I[gc.get_threshold] --> J[获取回收阈值]
K[gc.get_count] --> L[获取各代对象数]
end
7. 内存泄漏检测流程
graph TD
A[开始检测] --> B[获取内存快照]
B --> C[执行操作]
C --> D[再次获取快照]
D --> E[对比快照]
E --> F{内存增长?}
F -->|是| G[可能存在泄漏]
F -->|否| H[内存正常]
G --> I[使用 gc.get_referrers 追踪]
I --> J[找出引用链]
J --> K[修复泄漏]
graph TD
A[开始检测] --> B[获取内存快照]
B --> C[执行操作]
C --> D[再次获取快照]
D --> E[对比快照]
E --> F{内存增长?}
F -->|是| G[可能存在泄漏]
F -->|否| H[内存正常]
G --> I[使用 gc.get_referrers 追踪]
I --> J[找出引用链]
J --> K[修复泄漏]
8. 内存优化策略
graph TD
A[内存优化] --> B{对象类型?}
B -->|小对象| C[使用 __slots__]
B -->|大数据| D[使用生成器]
B -->|字符串| E[使用 sys.intern]
B -->|循环引用| F[使用 weakref]
C --> G[减少内存开销]
D --> H[避免一次性加载]
E --> I[复用字符串对象]
F --> J[避免内存泄漏]
graph TD
A[内存优化] --> B{对象类型?}
B -->|小对象| C[使用 __slots__]
B -->|大数据| D[使用生成器]
B -->|字符串| E[使用 sys.intern]
B -->|循环引用| F[使用 weakref]
C --> G[减少内存开销]
D --> H[避免一次性加载]
E --> I[复用字符串对象]
F --> J[避免内存泄漏]
9. 内存使用可视化
内存使用示意图:
┌─────────────────────────────────────────────────────────┐
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 对象堆 │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ 对象 1 │ │ 对象 2 │ │ 对象 3 │ ... │ │
│ │ └─────────┘ └─────────┘ └─────────┘ │ │
│ │ │ │
│ │ 第 0 代: 700 个对象 │ │
│ │ 第 1 代: 10 个对象 │ │
│ │ 第 2 代: 10 个对象 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ gc.garbage │ │
│ │ ┌─────────┐ ┌─────────┐ │ │
│ │ │ 无法回收 │ │ 无法回收 │ ... │ │
│ │ └─────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘