大家好!💡还记得你第一次写 Python 代码的时候吗?那种随意创建变量、毫无节制地 new 一个又一个对象的快感,简直让人沉迷! 可是,当代码运行变慢、内存占用飙升时,你才会意识到:
“诶?怎么 Python 还会吃这么多内存?!”
今天,我们就来聊聊 Python 的内存管理,帮你写出更高效、优化内存占用的 Python 代码!🎯
包含编程资料、学习路线图、源代码、软件安装包等!【点击这里免费领取】!
1. Python 的内存管理机制
Python 内部使用 引用计数(Reference Counting) 和 垃圾回收(Garbage Collection, GC) 机制来管理内存。
1.1 引用计数
每个对象都有一个“计数器”,记录它被多少个变量引用。一旦引用计数归零,Python 立刻释放这个对象的内存。
import sys
a = [] # 创建一个列表对象
print(sys.getrefcount(a)) # 输出 2(因为 sys.getrefcount() 也会额外增加一次引用)
b = a # 变量 b 也指向同一个列表
print(sys.getrefcount(a)) # 输出 3
del a
print(sys.getrefcount(b)) # 输出 2
del b # 引用计数归零,内存被释放
💡 注意:sys.getrefcount()
的结果比你想象的多 1,因为它本身也会创建一个临时引用!
1.2 垃圾回收(GC)
Python 采用 分代回收,对象被分成三代:新生代、中生代、老生代。垃圾回收主要针对循环引用的情况。
import gc
class A:
def __init__(self):
self.ref = None
obj1 = A()
obj2 = A()
obj1.ref = obj2
obj2.ref = obj1 # 形成循环引用
del obj1, obj2 # 引用计数没有归零,Python 需要 GC 来清理
gc.collect() # 手动触发垃圾回收
2. Python 内存优化技巧
2.1 使用 __slots__
限制对象属性
默认情况下,Python 的对象使用 动态字典(__dict__
) 存储属性,占用大量内存。如果你的类属性是固定的,可以用 __slots__
优化,之前花姐在其它文章中提到过。
class NormalClass:
pass
class SlotClass:
__slots__ = ['name', 'age'] # 仅允许 name 和 age 两个属性
obj1 = NormalClass()
obj1.name = "花姐"
obj1.age = 18
obj1.gender = "女" # 允许动态添加新属性
obj2 = SlotClass()
obj2.name = "花姐"
obj2.age = 18
# obj2.gender = "女" # ❌ AttributeError: 'SlotClass' object has no attribute 'gender'
__slots__
会让 Python 不再为对象创建 __dict__
,从而减少内存占用。
2.2 避免不必要的临时变量
Python 解释器会缓存一些常见的对象,例如 小整数(-5
到 256
在 Python 3.9 及以前的版本),以及部分 短字符串。
但在 Python 3.10+ 之后,整数的缓存范围 可能更大,具体行为依赖于 Python 实现。
# 可能被缓存(具体范围取决于 Python 版本)
a = 256
b = 256
print(a is b) # True
# 可能不被缓存
a = 257
b = 257
print(a is b) # 3.9 以前通常 False,3.10+ 可能 True
结论:Python 会缓存小整数,但具体范围视 Python 版本而定,不建议过分依赖此特性!
2.3 使用生成器代替列表
如果你只需要 逐个获取数据,而不是一次性加载所有数据,请用 生成器 代替列表。
# 占用大量内存的方式
nums = [i for i in range(10**6)]
# 更优的方式(惰性加载)
def num_generator():
for i in range(10**6):
yield i
gen = num_generator()
为什么? 生成器不会一次性把所有数据存入内存,而是每次 yield
一个值,这样可以大幅降低内存占用!🎉
2.4 使用 array
代替列表存储大量数值
如果你需要存储大量的数值,使用 array
模块比 list
更节省内存。
import array
# 创建一个存储 int 类型的数组,比列表更节省内存
arr = array.array('i', range(10**6))
2.5 使用 deque
代替列表进行队列操作
collections.deque
具有更高效的 头部插入和删除 操作,比 list
的 pop(0)
和 insert(0, x)
更优。
from collections import deque
dq = deque(range(10**6))
dq.appendleft(-1) # O(1) 复杂度
# 而 list.insert(0, -1) 是 O(n),在大规模数据下性能差距明显
3. 释放不用的内存
3.1 手动释放变量
Python 采用 自动垃圾回收,但如果你想主动释放大对象,建议使用 del
并 调用 gc.collect()
。
import gc
data = [i for i in range(10**6)]
del data # 删除变量
gc.collect() # 强制触发垃圾回收
在大数据处理中,这个方法可以 显著减少内存占用!
总结
-
Python 采用 引用计数 + 垃圾回收 来管理内存。
-
使用
__slots__
可以节省对象的 属性存储空间。 -
避免不必要的临时变量,Python 会缓存小整数,但范围 依赖 Python 版本。
-
用生成器替代列表,节省内存!
-
用
array
代替list
存储大量数值,提高内存效率。 -
用
deque
代替list
进行队列操作,提高性能。 -
手动释放大对象,使用
del
+gc.collect()
及时清理。
希望今天的内容对你有帮助!