我们知道,计算机的内存是有限的,我们让计算机帮我们干活,也就是处理数据,这些数据会占用计算机的内存,那么如果不合理的使用有限的内存,很快就会内存占满,导致计算机干不动活了。
那么在我们让计算机帮我们跑程序的时候,会进行大量的数据存储和计算,占用的内存空间,如果不及时的清理,那么就会出现内存泄露(我看有的文章或者视频说是内存溢出,但是我觉得不是,内存泄露就是申请的内存空间没有被释放,导致一直占着这个坑,不干活,所以叫内存泄露,而内存溢出,就是比如我需要给一个数据什么一块内存空间,但是这个内存空间不够放的,那么这个就是内存溢出。) 回到正题,内存本来就是有限的,你再给我泄露一点,那么可用的就变得更少了。
所以,为了解决合理利用内存空间,每个语言都有自己的内存回收机制,也就是垃圾回收机制,GC(garbage collection),Python也一样。
Python的垃圾回收机制是由它变量(其实叫对象,因为Python中一切皆对象)定义的方式来决定的,我们知道Python的变量声明的时候,是不指定类型的,声明的时候必须赋值,否则会报错,并且Python的变量声明之后,是给变量一个值的引用,并不是和c语言一样,直接就给这个变量分配块空间存放它的值了。 声明一个变量:a = 1,其实就是在内存中开辟一个空间放置值1,然后把值1的引用,也就是相当于地址的东西给a,那么a就能够访问1.此时1的被引用的次数就变成了1. 如果这个时候,再声明一个变量:b=a.那么就相当于b也拿到了1的引用,它们同时指向1,这个时候1的引用次数为:2 那么,如果我们声明了很多变量,内存占用也会增多,可用的变少,内存中的数据肯定分为:已经不使用的和正在使用的,那么计算机如何知道哪些被使用,哪些没有被使用呢,那就是引用,如果一块内存中的数据,没有被引用,也就是引用的次数为0,那么就会认为这个数据是垃圾数据,大于0次数的引用认为是有用的数据,垃圾数据肯定是及时清理掉,才能释放掉内存空间,给别的程序使用。 所以Python的垃圾回收机制就是当一块内存中的数据被申请后,他会有一个变量叫refchain变量,用来存放这个数据被引用的次数,被引用一次,就会加1.删除引用,就会减1,当减为0的时候,这个数据就会被Python的垃圾回收器给回收了,这块内存就会被释放,给别人使用。
上面的垃圾回收机制看着没什么问题,挺简单的。但是有一种特殊情况的数据,会被漏掉,什么样的数据呢,就是普通的数据,只是这个数据被别的数据引用,别的数据还引用它。其实这两块数据并没有在外部被使用,导致它们自己形成了一个环,这个环就叫做循环引用。 比如下面的代码就会导致形成环:
a = [1]
b = [2]
a.append(b)
b.append(a)
print(a,b)
在内存中引用是下面这样的: 可以看到上面变量a和变量b分别指向两块内存空间,这两块内存空间,内部互相引用。如果这个时候,del a 和del b.只会是下面这样: 也就是a和b对这两块的内存的引用都切断了,但是这两块内存的引用次数仍然不为0,就是因为它们内部互相引用,这样的话,Python的垃圾回收机制就会认为这个是个有用的数据,会保留,导致这块内存在程序运行期间一直存在,这样肯定是不行的,如果存在很多这样的数据,那么就会导致内存被浪费很多。为了解决这个循环引用的问题。就引出了标记-清除的一种垃圾回收方案。
在介绍标记-清除之前,再给一个例子看下,当一个数据在内存中的引用为0时,会自动调用__del__方法;
class INT(int):
def __del__(self):
print("我被释放了")
a = INT(1)
print(a,type(a))
a = 3
print(a,type(a))
运行结果:
1 <class '__main__.INT'>
我被释放了
3 <class 'int'>
可以看到在把3的引用给a的时候,1的引用已经没了,这个时候会自动去调用del方法,而我们重写了这个方法,所以能够看到打印。
接下来介绍标记-清除:
上面的介绍说:标记-清除是为了解决垃圾回收中循环引用的问题,就是说
|