上一章我們講過,不管是直接引用還是間接引用,只要引用計數(shù)為0的時候,就會被垃圾回收機(jī)制回收,但是這種工作方式是有問題的。
舉個例子,我現(xiàn)在定義兩個列表,l1和l2。
然后使用append()功能,l1.append。這個方法我們后面會講,現(xiàn)在可以先了解一下。它的作用就是給l1這個列表的最后添加一個元素。append()是這個列表的功能,只要是列表都有這個功能。然后我寫一個‘c’在括號里。
這就相當(dāng)于把‘c’添加到了l1里面,原來l1里面只有‘a(chǎn)’,‘b’這兩個元素,現(xiàn)在添加之后就有三個元素了?,F(xiàn)在我們來打印一下l1看,結(jié)果就是[‘a’,’b’,’c’]。
現(xiàn)在我把這個‘c’改成l2,就是把這個l2添加到l1里面,相當(dāng)于l2這個列表嵌套在了l1里面。來打印看一下。
在內(nèi)存里面這個l1里面放的就是0號索引對應(yīng)‘a(chǎn)’的內(nèi)存地址,1號索引對應(yīng)‘b’的內(nèi)存地址,2號索引對應(yīng)l2的內(nèi)存地址對吧,現(xiàn)在我們打印一下l2的id,和l1的2號索引的id,他們倆應(yīng)該都是一樣的,因?yàn)槎贾幌肓送粋€內(nèi)存地址。
現(xiàn)在我再做一件事,l2.append(l1),然后再打印下l2,結(jié)果就變成這樣了。
先不管它長什么樣子,我們先來看這個l2的第三個元素的id和l1的id是不是一樣的。結(jié)果還是一樣的,說明它們都指向了同一個內(nèi)存地址。
現(xiàn)在問題來了,我們剛剛把l2添加到了l1里面,所以l1里面存了l2的內(nèi)存地址。然后又把l1添加到了l2里面,所以l2里面又存了l1的內(nèi)存地址。你中有我,我中有你,互相交融。
這兩個列表中存在一種互相引用關(guān)系,這就叫循環(huán)引用,循環(huán)引用會導(dǎo)致非常致命的問題。現(xiàn)在我這樣,del l1,這個del不是刪除l1,它是解除了l1這個變量名和它指向的這個列表的綁定關(guān)系,也就是前面這個列表的引用計數(shù)減少了一個,但是前面這個列表的應(yīng)用技術(shù)并沒有變成0,因?yàn)楹竺孢@個列表還對他有間接引用。
現(xiàn)在我再寫一個del l2,l2這個變量名和后面的這個列表也解除了綁定關(guān)系,但是它的引用計數(shù)也沒變成0吧,因?yàn)樗磺懊孢@個列表間接引用了一次。
現(xiàn)在我們已經(jīng)把這兩個列表的引用全部解除了,但是它們身上的引用計數(shù)并沒有變成0,你仔細(xì)想一下,它們身上只有他們互相的間接引用我們再也沒有辦法取到這兩個列表了。
這時候循環(huán)引用的恐怖之處就來了,前面我們講了垃圾回收機(jī)制,會回收引用計數(shù)為0的內(nèi)存空間,但是我們現(xiàn)在沒有辦法可以取到這兩個列表,按理說這就是一個垃圾,就應(yīng)該被回收掉,可是這兩個列表的引用計數(shù)又不為0,所以這兩個列表這時候就變成了一個永遠(yuǎn)回收不了的垃圾,永遠(yuǎn)占著這兩塊內(nèi)存空間。
這就叫內(nèi)存泄露,在寫代碼的過程中這種情況是一定不允許發(fā)生的,由于循環(huán)引用會導(dǎo)致內(nèi)存泄露問題,所以Python提供了一種解決方案,叫標(biāo)記清除,但是我們下一章再講!
未經(jīng)允許不得轉(zhuǎn)載:445IT之家 » Python循環(huán)引用之內(nèi)存泄漏