Python的内存管理由解释器自动完成,核心依赖引用计数、垃圾回收机制以及内存池技术。我们在写代码时虽然不需要手动申请和释放内存,但了解其原理能帮助我们规避一些常见的性能与资源问题。

引用计数是如何工作的
每个Python对象内部都维护着一个引用计数器。当有新的变量指向该对象时,计数加一;当引用被销毁或重新指向其他对象时,计数减一。计数为零的对象会被立即回收。
import sys a = [1, 2, 3] # 创建一个列表对象 print(sys.getrefcount(a)) # 查看引用计数,注意函数调用本身也会临时增加计数 b = a # 增加一个新的引用 print(sys.getrefcount(a)) del b # 删除引用 print(sys.getrefcount(a))
循环引用与垃圾回收
引用计数无法处理循环引用的情况,例如两个对象互相引用导致计数永远不为零。此时Python的垃圾回收器(GC)会定期扫描并清理这些不可达对象。
- GC主要使用分代回收策略,将对象分为三代,新对象在第0代,存活越久越往后移。
- 可以通过gc模块手动触发回收或调整阈值。
import gc
class Node:
def __init__(self):
self.ref = None
n1 = Node()
n2 = Node()
n1.ref = n2
n2.ref = n1 # 形成循环引用
del n1, n2 # 引用计数不为0,但已不可达
gc.collect() # 手动执行垃圾回收,清理循环引用对象
内存池与小块内存分配
Python为了提升效率,对小于256字节的小对象使用了内存池(pymalloc)。这些对象不会直接向操作系统申请内存,而是从空闲块中分配,减少系统调用开销。
| 对象大小 | 分配方式 |
|---|---|
| 小于256字节 | 内存池 pymalloc |
| 大于256字节 | 标准C库 malloc |
如何主动观察内存使用
在调试阶段,我们可以利用tracemalloc模块追踪内存分配来源,找出占用较高的代码位置。
import tracemalloc
tracemalloc.start()
x = [i for i in range(10000)] # 占用一定内存的操作
snapshot = tracemalloc.take_snapshot()
top = snapshot.statistics('lineno')
for stat in top[:3]:
print(stat) # 输出内存占用最高的几行代码信息
编写更省内存的代码
在实际开发中,可以优先使用生成器而不是列表,使用__slots__限制实例属性,从而减少不必要的内存开销。
class Point:
__slots__ = ['x', 'y'] # 禁止动态添加属性,节省内存
def __init__(self, x, y):
self.x = x
self.y = y
p = Point(1, 2)
Python的内存管理以自动化为主,但掌握引用计数、GC与内存池的基本原理,能让我们在面临大规模数据或长时运行服务时做出更合理的代码设计。