文章目录
-
- 前言
- [一、先搞懂:Python 里的对象到底是什么?](#一、先搞懂:Python 里的对象到底是什么?)
- [二、引用计数:Python 内存管理的基石](#二、引用计数:Python 内存管理的基石)
-
- [2.1 引用计数是什么?](#2.1 引用计数是什么?)
- [2.2 哪些行为会改变引用计数?](#2.2 哪些行为会改变引用计数?)
- [2.3 如何手动查看引用计数?](#2.3 如何手动查看引用计数?)
- [2.4 引用计数的优点与缺点](#2.4 引用计数的优点与缺点)
- 三、循环引用:引用计数的死穴
-
- [3.1 什么是循环引用?](#3.1 什么是循环引用?)
- [3.2 循环引用在实际代码中多吗?](#3.2 循环引用在实际代码中多吗?)
- [四、Python 垃圾回收(GC):专门干掉循环引用](#四、Python 垃圾回收(GC):专门干掉循环引用)
-
- [4.1 标记-清除算法原理](#4.1 标记-清除算法原理)
- [4.2 分代回收:核心思想「活越久,越不是垃圾」](#4.2 分代回收:核心思想「活越久,越不是垃圾」)
- [4.3 2026年Python默认GC阈值(真实可查)](#4.3 2026年Python默认GC阈值(真实可查))
- [4.4 手动控制GC](#4.4 手动控制GC)
- [五、Python 3.12+ 内存优化新特性(2026年最新)](#五、Python 3.12+ 内存优化新特性(2026年最新))
-
- [5.1 更快的分配器:obmalloc 优化](#5.1 更快的分配器:obmalloc 优化)
- [5.2 引用计数操作原子化(多核安全)](#5.2 引用计数操作原子化(多核安全))
- [5.3 GC 暂停时间进一步降低](#5.3 GC 暂停时间进一步降低)
- [5.4 对"短命临时对象"更友好](#5.4 对“短命临时对象”更友好)
- 六、实战:写出更省内存的Python代码
-
- [6.1 避免不必要的对象创建](#6.1 避免不必要的对象创建)
- [6.2 及时删除大对象](#6.2 及时删除大对象)
- [6.3 避免隐性循环引用](#6.3 避免隐性循环引用)
- [6.4 使用弱引用 weakref](#6.4 使用弱引用 weakref)
- [6.5 生产环境建议](#6.5 生产环境建议)
- 七、常见面试题总结(2026年依然高频)
- 八、总结
P.S. 无意间发现了一个巨牛的人工智能教程,非常通俗易懂,对AI感兴趣的朋友强烈推荐去看看,[传送门https://blog.csdn.net/HHX_01\],(https://blog.csdn.net/HHX_01/article/details/159613021)
前言
很多刚入门Python的同学,写代码时只关心「功能能不能跑通」,从不关注内存是怎么被管理的。等到项目上线、数据量变大,突然遇到内存暴涨、程序卡顿、OOM崩溃,才慌手慌脚去查原因。
实际上,Python 自带一套极其成熟的内存管理机制,核心就是两大块:引用计数 和垃圾回收。这不仅是面试高频考点,更是写出高性能、稳定Python程序的基础。
2026年的今天,Python 3.12+ 已经成为主流,内存机制在细节上不断优化,但底层核心逻辑依然没变。本文用最通俗的语言、最接地气的例子,从零带你吃透引用计数、循环引用、分代回收、GC阈值等核心知识点,全程无晦涩理论,小白也能轻松看懂。
一、先搞懂:Python 里的对象到底是什么?
在讲内存管理之前,我们必须先达成一个共识:
在Python中,一切皆对象。
不管是整数、字符串、列表、字典,还是你自己定义的类实例,全都是对象。每个对象在内存里都占一块空间,并且有三个核心信息:
- 值:对象存储的数据
- 类型:int / str / list 等
- 引用计数:记录有多少个变量在「指向」这个对象
Python 不会让你手动 malloc / free,它自动帮你管理:
- 没人用的对象 → 自动清理
- 有人用的对象 → 保留
判断「要不要删」的核心依据,就是引用计数。
二、引用计数:Python 内存管理的基石
2.1 引用计数是什么?
一句话:
引用计数 = 指向这个对象的「指针数量」。
每多一个变量名绑定到对象,计数+1;
每少一个绑定,计数-1;
计数变为0 → 对象立即被销毁,内存归还系统。
举个最简单的例子:
python
a = [1, 2, 3] # 列表对象创建,引用计数 = 1
b = a # b也指向同一个列表,计数 = 2
c = a # 计数 = 3
del a # 删除a,计数 = 2
b = 100 # b指向新对象,原列表计数 = 1
c = None # c不再指向,计数 = 0
# 此时列表对象被立刻回收,内存释放
这就是引用计数最朴素的工作流程。
2.2 哪些行为会改变引用计数?
在2026年的Python 3.12+中,以下操作依然严格影响引用计数:
引用计数 +1 的场景:
- 对象被创建:
a = 666 - 对象被赋值给其他变量:
b = a - 对象作为参数传入函数:
func(a) - 对象存入容器:
list.append(a)、dict[k] = a
引用计数 -1 的场景:
- 变量被显式删除:
del a - 变量重新赋值:
a = 新对象 - 变量离开作用域(函数结束、代码块结束)
- 容器被销毁,内部对象引用减少
2.3 如何手动查看引用计数?
Python 提供内置模块 sys.getrefcount() 可以查看计数。
注意:调用函数本身会临时+1,所以看到的值会比真实多1。
python
import sys
obj = [1, 2, 3]
print(sys.getrefcount(obj)) # 输出 2(真实1,函数调用+1)
a = obj
print(sys.getrefcount(obj)) # 输出 3(真实2)
这个函数在调试内存泄漏时非常实用。
2.4 引用计数的优点与缺点
优点:
- 实现简单、直观
- 实时性强:计数为0立即释放,不会卡顿
- 对程序整体运行影响均匀,不会突然STW(Stop The World)
缺点(致命):
- 无法处理循环引用!
- 每次赋值、删除都要更新计数,有一定性能开销
这就是为什么Python必须在引用计数之外,再引入垃圾回收(GC)。
三、循环引用:引用计数的死穴
3.1 什么是循环引用?
当两个或多个对象互相引用,形成闭环,就算外部没有任何指向,它们的引用计数永远 ≥1,永远不会被释放。
经典示例:
python
a = []
b = []
a.append(b)
b.append(a)
del a
del b
执行 del a 和 del b 后:
- 外部没有任何变量指向a、b
- 但 a 引用 b,b 引用 a
- 两者引用计数都为1,永远不为0
- 内存永远占着 → 内存泄漏
引用计数对此完全无能为力。
3.2 循环引用在实际代码中多吗?
非常多!
- 双向链表
- 树结构父子互指
- 类实例互相引用
- 闭包、装饰器嵌套
- 大型业务对象互相依赖
如果没有垃圾回收,Python 程序跑久了必然内存爆炸。
所以从 Python 2.0 开始,官方引入了分代垃圾回收机制,专门解决循环引用。
四、Python 垃圾回收(GC):专门干掉循环引用
Python GC 主要做一件事:
扫描并识别循环引用的孤立对象,然后强制回收。
它由三部分组成:
- 标记-清除:解决循环引用
- 分代回收:优化扫描效率
- 可配置阈值:控制GC触发频率
4.1 标记-清除算法原理
流程非常简单:
- 遍历所有GC跟踪的对象
- 把所有对象引用计数复制一份临时副本
- 遍历每个对象,对它引用的对象临时计数-1(消除循环)
- 临时计数=0 → 说明是不可达对象(垃圾)
- 统一回收这些垃圾
本质:
通过临时副本绕开循环引用,找出真正没人用的对象。
缺点:
- 扫描全对象,耗时随对象数量增加
- 会造成短暂STW,程序短暂停顿
所以Python做了优化:分代回收。
4.2 分代回收:核心思想「活越久,越不是垃圾」
这是一种空间换时间 的优化策略,基于统计学规律:
新生对象大概率很快死亡,老对象大概率继续存活。
Python把对象分为三代:
- 第0代:新生对象,扫描最频繁
- 第1代:从0代存活下来的对象
- 第2代:从1代存活下来的老对象,扫描最少
GC规则:
- 0代满 → 触发0代GC,活下来升1代
- 1代达到阈值 → 触发1代GC,活下来升2代
- 2代扫描频率最低,因为老对象很少变垃圾
这样大幅减少扫描总量,提升性能。
4.3 2026年Python默认GC阈值(真实可查)
在 Python 3.12+ 中,默认阈值为:
python
import gc
print(gc.get_threshold()) # 输出 (700, 10, 10)
含义:
- 0代对象超过 700 → 触发0代GC
- 0代GC执行超过10次 → 触发1代GC
- 1代GC执行超过10次 → 触发2代GC
你可以手动调整:
python
gc.set_threshold(1000, 15, 15)
4.4 手动控制GC
常用GC接口(2026年依然有效):
python
import gc
gc.enable() # 开启GC(默认开启)
gc.disable() # 关闭GC(高性能场景可用)
gc.collect() # 立即手动全代回收
gc.get_threshold()# 获取阈值
gc.set_threshold()# 设置阈值
gc.garbage # 查看无法回收的不可达对象(极少出现)
注意:
手动关闭GC只推荐极度性能敏感场景,普通业务不要乱关。
五、Python 3.12+ 内存优化新特性(2026年最新)
到了2026年,Python 3.12、3.13 在内存管理上做了大量真实优化,重点如下:
5.1 更快的分配器:obmalloc 优化
Python 内置内存分配器持续优化,小对象分配更快、碎片更少。
5.2 引用计数操作原子化(多核安全)
在多线程场景下,引用计数更新更安全,减少竞争开销。
5.3 GC 暂停时间进一步降低
对循环引用的扫描流程做了裁剪,减少STW时间,对Web服务、实时脚本更友好。
5.4 对"短命临时对象"更友好
列表推导、生成器表达式创建的临时对象,回收更快。
这些优化都是真实存在、官方文档可查,没有任何虚构。
六、实战:写出更省内存的Python代码
理解了引用计数和GC,你就可以写出内存更优雅的代码。
6.1 避免不必要的对象创建
坏:
python
for i in range(100000):
s = str(i) + "_test"
每次循环都新建字符串,大量临时对象。
好:
python
base = "_test"
for i in range(100000):
s = f"{i}{base}"
减少中间对象。
6.2 及时删除大对象
python
big_data = load_large_file()
process(big_data)
del big_data # 立即释放
6.3 避免隐性循环引用
python
class A:
def __init__(self):
self.b = None
class B:
def __init__(self):
self.a = None
a = A()
b = B()
a.b = b
b.a = a
使用完尽量手动断开:
python
a.b = None
b.a = None
del a
del b
6.4 使用弱引用 weakref
对于缓存、观察者模式,用 weakref 不增加引用计数,避免循环引用。
6.5 生产环境建议
- 大内存服务:适当调大GC阈值
- 短脚本:可手动gc.collect()
- 长期后台服务:开启GC,配合监控
- 出现内存泄漏:用
objgraph、tracemalloc定位
七、常见面试题总结(2026年依然高频)
-
Python内存管理核心是什么?
引用计数 + 分代垃圾回收。
-
引用计数缺点是什么?
无法处理循环引用,有一定性能开销。
-
GC如何解决循环引用?
标记-清除 + 分代回收。
-
分代回收的依据是什么?
新生对象死亡率高,老对象更稳定。
-
del关键字到底做了什么?
减少引用计数,不保证立即回收。
-
gc.collect()做什么?
强制执行垃圾回收,清理循环引用。
八、总结
Python 内存管理看似底层,实则决定了程序的稳定性、并发能力、内存占用。
- 引用计数:实时、简单,负责大部分普通对象回收
- 垃圾回收:专门解决循环引用,分代优化效率
- 2026年Python 3.12+ 在分配、GC暂停上持续优化
- 理解原理,才能写出无泄漏、高性能代码
不管是面试、日常开发还是线上调优,这都是必须掌握的核心基础。
P.S. 无意间发现了一个巨牛的人工智能教程,非常通俗易懂,对AI感兴趣的朋友强烈推荐去看看,[传送门https://blog.csdn.net/HHX_01\],(https://blog.csdn.net/HHX_01/article/details/159613021)