一、引子
首先,我们从两个例子入手垃圾回收机制:
a = 1000
b = 2000
a = b
a = 100
b = 200
a = b
这两段代码的功能都是“把b变量的值赋值给a变量”,但是在Python的底层逻辑上,这两段代码的实现过程确是有所不同的。
过程:第一段代码是在存储空间中先创建了一个对象:整数1000,之后让变量a指向这个存储空间;同理变量b;当执行 a = b 语句时,变量a由原来的指向整数1000,变为了指向整数2000,同时,刚刚创建的存储整数1000的存储空间,也被垃圾回收机制回收。
第二段代码中,整数对象100
和200
受到Python整数缓存机制的影响:通常对于 -5到255之间的整数,python会预先创建他们并存储,所以在执行 a = b语句后,整数100并没有被回收
二、垃圾回收机制
有了上面的例子后,我们来正式学习Python的垃圾回收机制
Python的垃圾回收机制(GC) 主要依赖于引用计数法为基础,并辅以“代”(generation)收集机制来处理循环引用的问题。这一机制确保了不再使用的对象能够被正确地识别并进行回收,释放内存。
1.引用计数
Python中每个对象在创立之初,都会获得一个引用计数,用来记录有多少个引用指向这个对象(不同于c语言的变量创建、存储机制)。
当创建一个对象并把他赋值给一个变量时,该对象的引用计数变为1。如果有另一个变量被赋值为这个对象,该对象的引用计数加1。同样的,若是本来指向该对象的引用,现在不再指向它,则引用计数减1。当一个对象的引用计数变为0时,意味着没有任何引用指向这个对象,它就会被垃圾回收器立即回收。
2.解决循环引用的分代收集机制
有了基于引用计数的回收机制后,我们不禁思考一个问题,若是两个对象之间,或者多个对象之间互相引用,但是却没有任何一个变量来指向他们中的任意一个,那么会造成他们不会被回收掉=>内存泄漏
为了解决这一问题,Python引入了分代收集机制:
在这个系统中,所有的对象都被分配到三个“代”(generation)中的一个:0代、1代和2代。一般情况下,新创建的对象被放入0代中。如果在某次0代的垃圾回收过程中,对象存活下来,它就会被移动到1代;同样,从1代存活下来的对象会被移动到2代。因为预期较老的对象(在高代中的对象)较少会变成垃圾,所以较高代的垃圾回收频率会低于较低代的。
垃圾回收器通过检查对象之间的引用关系,来识别循环引用。如果一组对象之间只有相互引用,而没有任何外部引用指向这组对象,这组对象就可以被认为是可回收的。
3.补充(触发条件)
Python的垃圾回收可以被显式地触发,也可以根据需要自动触发。自动触发的条件基于分配和释放对象的数量,以及各代中对象的数量。可以通过gc模块调整垃圾回收的行为,如 启用或禁用垃圾回收、手动触发垃圾回收,以及调整代之间的阈值等。