python內存泄露
起因
內存泄露指由於疏忽或錯誤造成程序未能釋放已經不再使用的內存的情況。內存泄漏並非指內存在物理上的消失,而是應用程序分配某段內存后,由於設計錯誤,失去了對該段內存的控制,因而造成了內存的浪費。導致程序運行速度減慢甚至系統崩潰等嚴重后果。有 del() 函數的對象間的循環引用是導致內存泄漏的主凶
方案
不使用一個對象時使用:delobject 來刪除一個對象的引用計數就可以有效防止內存泄漏問題。通過Python 擴展模塊 gc 來查看不能回收的對象的詳細信息。可以通過 sys.getrefcount(obj) 來獲取對象的引用計數,並根據返回值是否為 0 來判斷是否內存泄漏。
但是由於gc垃圾收集機制,要遍歷所有被垃圾收集器管理的python對象(包括垃圾和非垃圾對象),該過程比較耗時可能會造成程序卡頓,會對某些對內存、cpu要求較高的場景造成性能影響。那怎么才能優雅地避免內存泄露呢?
編寫安全的代碼
比如對於下面發生內存泄露的cycle_ref函數,在函數結束前解除循環引用,即可解決內存泄露問題。
def cycle_ref():
a1 = A()
a2 = A()
a1.child = a2
a2.child = a1
# 解除循環引用,避免內存泄露
a1.child = None
a2.child = None
但是對於上述方法,我們有可能會忘記那一兩行無關緊要的代碼而造成災難性后果,畢竟老虎也有打盹的時候。那怎么辦?不要着急,Python已經為我們考慮到這點:弱引用。
弱引用
Python標准庫提供了weakref模塊,弱引用不會在引用計數中計數,其主要目的是解決循環引用。並非所有的對象都支持weakref,例如list和dict就不支持。下面是weakref比較常用的方法:
"""
1. class weakref.ref(object[, callback]) :創建一個弱引用對象,object是被引用的對象,callback是回調函數(當被引用對象被刪除時,調用該回調函數)
2.weakref.proxy(object[, callback]):創建一個用弱引用實現的代理對象,參數同上
3.weakref.getweakrefcount(object) :獲取對象object關聯的弱引用對象數
4.weakref.getweakrefs(object):獲取object關聯的弱引用對象列表
5.class weakref.WeakKeyDictionary([dict]):創建key為弱引用對象的字典
6.class weakref.WeakValueDictionary([dict]):創建value為弱引用對象的字典
7.class weakref.WeakSet([elements]):創建成員為弱引用對象的集合對象
"""
同樣對於上面發生內存泄露的cycle_ref函數,使用weakref稍加改造,便可更安全地解決內存泄露問題:
import weakref
class A(object):
def __init__(self):
self.data = [x for x in range(100000)]
self.child = None
def __del__(self):
pass
def cycle_ref():
a1 = A()
a2 = A()
a1.child = weakref.proxy(a2)
a2.child = weakref.proxy(a1)
if __name__ == '__main__':
import time
while True:
time.sleep(0.5)
cycle_ref()