Python垃圾回收包括引用計數、標記清除和分代回收
引用計數
引用計數是一種垃圾收集機制,當一個python對象被引用時,引用計數加1,當一個對象的引用為0時,該對象會被當做垃圾回收。
fromsysimportgetrefcount
l1=[1,2,3]
print(getrefcount(l1))#查看引用計數
l2=l1
print(getrefcount(l2))
執行結果:
2
3
在使用getrefcount()的時候,變量作為參數傳進去,會多一次引用。
del語句會刪除對象的一個引用。請看下面的例子:
fromsysimportgetrefcount
classTestObjectA():
def__init__(self):
print("hello!!!")
def__del__(self):
print("bye!!!")
a=TestObjectA()
b=a
c=a
print(getrefcount(c))
dela
print(getrefcount(c))
delb
print(getrefcount(c))
delc
print("666")
執行結果:
hello!!!
4
3
2
bye!!!
666
方法__del__的作用是當對象被銷毀時調用。其中dela刪除了變量a,但是對象TestObjectA仍然存在,它還被b和c引用,所以不會被回收,引用計數為0時會被回收。上面的例子中,將a,b,c都刪除后引用的對象被回收(打印“666”之前)。
另外重新賦值也會刪除對象的一個引用。
標記清除
如果出現了循環引用,引用計數方法就無法回收,導致內存泄漏。先來看下面的例子:
classTestObjectA(dict):
def__init__(self):
print("A:hello!!!")
def__del__(self):
print("A:bye!!!")
classTestObjectB(dict):
def__init__(self):
print("B:hello!!!")
def__del__(self):
print("B:bye!!!")
a=TestObjectA()
b=TestObjectB()
a['1']=b
b['1']=a
dela
delb
print("666")
執行結果:
A:hello!!!
B:hello!!!
666
A:bye!!!
B:bye!!!
上面的代碼存在循環引用,刪除a和b之后,它們的引用計數還是1,仍然大于0,不會被回收(打印“666”之后)。
標記清除可解決循環引用問題,從根對象(寄存器和程序棧上的引用)出發,遍歷對象,將遍歷到的對象打上標記(垃圾檢測),然后在內存中清除沒有標記的對象(垃圾回收)。上面的例子中,a和b相互引用,如果與其他對象沒有引用關系就不會遍歷到它,也就不會被標記,所以會被清除。
分代回收
如果頻繁進行標記清除會影響Python性能,有很多對象,清理了很多次他依然存在,可以認為,這樣的對象不需要經常回收,也就是說,對象存在時間越長,越可能不是垃圾。
將回收對象進行分代(一共三代),每代回收的時間間隔不同,其中新創建的對象為0代,如果一個對象能在第0代的垃圾回收過程中存活下來,那么它就被放入到1代中,如果1代里的對象在第1代的垃圾回收過程中存活下來,則會進入到2代。
gc模塊
以下三種情況會啟動垃圾回收:
·調用gc.collect():強制對所有代執行一次回收
·當gc模塊的計數器達到閥值的時候。
·程序退出的時候
gc模塊函數:
·gc.enable():啟用自動垃圾回收
·gc.disable():停用自動垃圾回收
·gc.isenabled():如果啟用了自動回收則返回True。
·gc.collect(generation=2):不設置參數會對所有代執行一次回收
·gc.set_threshold(threshold0[,threshold1[,threshold2]]):設置垃圾回收閾值
·gc.get_count():當前回收計數
·垃圾回收啟動的默認閾值
importgc
print(gc.get_threshold())
輸出:
(700,10,10)
700是垃圾回收啟動的閾值,對象分配數量減去釋放數量的值大于700時,就會開始進行垃圾回收,每10次0代垃圾回收,會導致一次1代回收;而每10次1代的回收,才會有1次的2代回收。可以使用set_threshold()方法重新設置。
以上內容為大家介紹了Python垃圾回收,希望對大家有所幫助,如果想要了解更多Python相關知識,請關注IT培訓機構:千鋒教育。http://www.dietsnews.net/