上一章我们讲过,不管是直接引用还是间接引用,只要引用计数为0的时候,就会被垃圾回收机制回收,但是这种工作方式是有问题的。
举个例子,我现在定义两个列表,l1和l2。
然后使用append()功能,l1.append。这个方法我们后面会讲,现在可以先了解一下。它的作用就是给l1这个列表的最后添加一个元素。append()是这个列表的功能,只要是列表都有这个功能。然后我写一个‘c’在括号里。
这就相当于把‘c’添加到了l1里面,原来l1里面只有‘a’,‘b’这两个元素,现在添加之后就有三个元素了。现在我们来打印一下l1看,结果就是[‘a’,’b’,’c’]。
现在我把这个‘c’改成l2,就是把这个l2添加到l1里面,相当于l2这个列表嵌套在了l1里面。来打印看一下。
在内存里面这个l1里面放的就是0号索引对应‘a’的内存地址,1号索引对应‘b’的内存地址,2号索引对应l2的内存地址对吧,现在我们打印一下l2的id,和l1的2号索引的id,他们俩应该都是一样的,因为都只想了同一个内存地址。
现在我再做一件事,l2.append(l1),然后再打印下l2,结果就变成这样了。
先不管它长什么样子,我们先来看这个l2的第三个元素的id和l1的id是不是一样的。结果还是一样的,说明它们都指向了同一个内存地址。
现在问题来了,我们刚刚把l2添加到了l1里面,所以l1里面存了l2的内存地址。然后又把l1添加到了l2里面,所以l2里面又存了l1的内存地址。你中有我,我中有你,互相交融。
这两个列表中存在一种互相引用关系,这就叫循环引用,循环引用会导致非常致命的问题。现在我这样,del l1,这个del不是删除l1,它是解除了l1这个变量名和它指向的这个列表的绑定关系,也就是前面这个列表的引用计数减少了一个,但是前面这个列表的应用技术并没有变成0,因为后面这个列表还对他有间接引用。
现在我再写一个del l2,l2这个变量名和后面的这个列表也解除了绑定关系,但是它的引用计数也没变成0吧,因为它被前面这个列表间接引用了一次。
现在我们已经把这两个列表的引用全部解除了,但是它们身上的引用计数并没有变成0,你仔细想一下,它们身上只有他们互相的间接引用我们再也没有办法取到这两个列表了。
这时候循环引用的恐怖之处就来了,前面我们讲了垃圾回收机制,会回收引用计数为0的内存空间,但是我们现在没有办法可以取到这两个列表,按理说这就是一个垃圾,就应该被回收掉,可是这两个列表的引用计数又不为0,所以这两个列表这时候就变成了一个永远回收不了的垃圾,永远占着这两块内存空间。
这就叫内存泄露,在写代码的过程中这种情况是一定不允许发生的,由于循环引用会导致内存泄露问题,所以Python提供了一种解决方案,叫标记清除,但是我们下一章再讲!
未经允许不得转载:445IT之家 » Python循环引用之内存泄漏