跳到主要内容

内存优化

问题

Python 程序占用内存过多怎么优化?有哪些内存分析工具?

答案

常用优化手段

1. __slots__ 减少实例内存

import sys

class WithDict:
def __init__(self, x, y):
self.x = x
self.y = y

class WithSlots:
__slots__ = ("x", "y")
def __init__(self, x, y):
self.x = x
self.y = y

print(sys.getsizeof(WithDict(1, 2).__dict__)) # 104 bytes
# WithSlots 没有 __dict__,节省约 40%

2. 生成器替代列表

# ❌ 大列表
data = [process(x) for x in range(10_000_000)] # 可能占用 GB 级内存

# ✅ 生成器
data = (process(x) for x in range(10_000_000)) # 几乎不占内存

3. 合适的数据结构

# 存储大量布尔值
import array
from array import array as typed_array

# list: 每元素 ~28 bytes
bools_list = [True] * 1_000_000

# array: 每元素 1 byte
bools_array = typed_array('b', [1] * 1_000_000)

# numpy: 更紧凑
import numpy as np
bools_np = np.ones(1_000_000, dtype=np.bool_) # 每元素 1 byte + 固定开销

内存分析工具

tracemalloc(标准库):

import tracemalloc

tracemalloc.start()

# 执行代码
data = [i ** 2 for i in range(100_000)]

snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics("lineno")

for stat in top_stats[:5]:
print(stat)
# 输出每行代码的内存分配量

memory_profiler(第三方):

# pip install memory_profiler
from memory_profiler import profile

@profile
def my_func():
a = [i for i in range(1_000_000)]
del a
b = {i: i for i in range(1_000_000)}
return b

# 输出每行的内存增量

objgraph(对象引用图):

import objgraph

# 查看内存中最多的对象类型
objgraph.show_most_common_types(limit=10)

# 找到特定对象的引用链(排查泄漏)
objgraph.show_backrefs(obj, max_depth=3)

常见面试问题

Q1: Python 内存泄漏怎么排查?

答案

  1. tracemalloc 对比两个时间点的快照,找到内存增长的位置
  2. objgraph 查找意外增长的对象类型和引用链
  3. gc.get_referrers() 查看谁在引用某个对象
  4. memory_profiler 逐行分析内存增量

Q2: del 语句会立即释放内存吗?

答案

del 只是删除引用(引用计数 -1),不一定立即释放内存:

  • 如果引用计数变为 0 → 立即释放
  • 如果还有其他引用 → 不释放
  • 释放的小内存块会被 pymalloc 缓存在 Pool 中,不一定归还给操作系统

相关链接