大家好,我是虎哥。
今天要和大家聊聊Python的内存管理。作为一个资深Python开发工程师,我觉得这其实是个展示技术的好机会——面试官一问,你可以先微微一笑,丢回一个问题:“您说的是官方的CPython解释器吗?”这是个小技巧,能直接展示出你对Python不同解释器的了解,先别管面试官自己知不知道CPython和别的解释器有啥区别,这自信得有。
那么,Python到底是如何处理内存管理的呢?用咱们程序员的话来说,Python的内存管理机制其实就是——“你写代码就行,内存我来管”。
换句话说,在Python中,内存空间的分配和释放是自动化的,这是由Python解释器在运行时动态进行的。这样的设计不仅省了开发人员的事儿,还能在一定程度上帮我们减少内存泄漏问题。
好啦,废话不多说,接下来就以CPython解释器为例,看Python内存管理的几个关键“武器”吧。
首先,引用计数就是CPython最基础的内存管理机制。说白了,Python里的每一个对象其实都是一个结构体,比如我们常见的PyObject
,这个结构体里有一个ob_refcnt
的成员变量,专门用来计数有多少个地方引用了这个对象。
代码里一旦创建了一个对象,引用计数就加1;如果有个新变量引用它,比如赋值给另一个变量,计数也会加1;要是把它作为参数传到函数里,引用计数还是加1。删除呢?也简单,你写个del
把这个引用删了,或者让引用重新指向其他对象,引用计数就会减1。
import sys
a = [1, 2, 3]
print(sys.getrefcount(a)) # 查看引用计数
执行这段代码,你会发现sys.getrefcount(a)
的结果比预想的多了1,这其实是因为getrefcount
函数调用时自己也增加了一个临时引用,这说明引用计数机制确实实时跟踪对象引用的变化。
这么做虽然自动化,但也不是完美的——引用计数对循环引用的情况可无能为力。你如果写个“左手倒右手”的循环引用,引用计数永远不会降到0,这就麻烦了。
那么,标记清理(Mark and Sweep)算法在这时就登场了,用来解决循环引用的问题。这套算法的核心思想是“标记+清理”,听起来有点中二哈?其实就是,Python会遍历所有对象,把可达对象标记出来,接着再扫描一遍,凡是没有标记为可达的,就收拾掉。
CPython底层维护了两个双端链表,链表A用来存放需要扫描的容器对象,链表B存放临时不可达的对象。链表A的节点有个gc_ref
变量,这个变量初始值等于引用计数。
当清理开始,A链表里的对象会把自己所引用对象的gc_ref
减1,如果减完之后某个对象的gc_ref
为0,就会被标记为“暂时不可达”并移动到链表B中;若不是0,则递归标记该对象的引用为可达。最终链表B中那些“孤独”的对象就被直接回收。
然后,我们来聊聊CPython垃圾回收系统中的“特级加速器”——分代回收(Generational GC)。Python的思路是这样的:“对象活得越久,成垃圾的概率越低。”因此,Python把对象划分为三代,分别是第0代、第1代和第2代,代表对象的“年龄”。
一个新创建的对象从第0代起步,每次在垃圾回收中幸存下来就升级一代。不同代的对象回收频率不同,0代回收最频繁,2代回收最少。为什么这么做呢?因为一个对象活得时间越长,它通常就是“靠谱”的,所以就不急着回收,免得浪费资源。
为了实现这一点,Python的垃圾回收模块(gc
)设置了一个“门槛”系统,每个代的垃圾回收频率由内存分配次数来决定。比如,你可以通过gc.get_threshold()
来查看不同代的门限值:
import gc
print(gc.get_threshold()) # 返回一个三元组 (700, 10, 10)
这个返回值的意思是:每进行700次内存分配操作后,Python会触发一次0代垃圾回收;每进行10次0代垃圾回收后会触发一次1代垃圾回收;每进行10次1代垃圾回收后会触发一次2代垃圾回收。你甚至可以用gc.set_threshold()
调整这几个门槛。
gc.set_threshold(750, 15, 15) # 调整门限
当然,门槛设太低也不好,频繁回收不仅效率低,还会导致性能下降,内存使用也不一定优化。合理的回收机制设置既能优化内存占用,也能保持代码的执行效率。
好,回到最初的问题,如果面试官直接问你“Python是如何实现内存管理的?”你可以这样回答:“Python通过引用计数、标记清理和分代回收来管理内存。引用计数是基础,但无法处理循环引用,因此Python还使用了标记清理算法。为了提高垃圾回收效率,Python又加入了分代回收机制,优先清理新生成的短命对象。”
对编程、职场感兴趣的同学,大家可以联系我微信:golang404,拉你进入“程序员交流群”。
资料包含了《IDEA视频教程》、《最全python面试题库》、《最全项目实战源码及视频》及《毕业设计系统源码》,总量高达650GB。全部免费领取!全面满足各个阶段程序员的学习需求。