国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Python > 正文

Python的內存泄漏及gc模塊的使用分析

2019-11-25 18:19:14
字體:
來源:轉載
供稿:網友

一般來說在 Python 中,為了解決內存泄漏問題,采用了對象引用計數,并基于引用計數實現自動垃圾回收。
由于Python 有了自動垃圾回收功能,就造成了不少初學者誤認為自己從此過上了好日子,不必再受內存泄漏的騷擾了。但如果仔細查看一下Python文檔對 __del__() 函數的描述,就知道這種好日子里也是有陰云的。下面摘抄一點文檔內容如下:

Some common situations that may prevent the reference count of an object from going to zero include: circular references between objects (e.g., a doubly-linked list or a tree data structure with parent and child pointers); a reference to the object on the stack frame of a function that caught an exception (the traceback stored in sys.exc_traceback keeps the stack frame alive); or a reference to the object on the stack frame that raised an unhandled exception in interactive mode (the traceback stored in sys.last_traceback keeps the stack frame alive).

可見,有 __del__() 函數的對象間的循環引用是導致內存泄漏的主兇
另外需要說明:對沒有 __del__() 函數的 Python 對象間的循環引用,是可以被自動垃圾回收掉的

如何知道一個對象是否內存泄漏了呢?

方法一、當你認為一個對象應該被銷毀時(即引用計數為 0),可以通過 sys.getrefcount(obj) 來獲取對象的引用計數,并根據返回值是否為 0 來判斷是否內存泄漏。如果返回的引用計數不為 0,說明在此刻對象 obj 是不能被垃圾回收器回收掉的。

方法二、也可以通過 Python 擴展模塊 gc 來查看不能回收的對象的詳細信息。


首先,來看一段正常的測試代碼:

#--------------- code begin --------------# -*- coding: utf-8 -*-import gcimport sysclass CGcLeak(object):  def __init__(self):    self._text = '#'*10  def __del__(self):    passdef make_circle_ref():  _gcleak = CGcLeak()#  _gcleak._self = _gcleak # test_code_1  print '_gcleak ref count0:%d' % sys.getrefcount(_gcleak)  del _gcleak  try:    print '_gcleak ref count1:%d' % sys.getrefcount(_gcleak)  except UnboundLocalError:    print '_gcleak is invalid!'def test_gcleak():  # Enable automatic garbage collection.  gc.enable()  # Set the garbage collection debugging flags.  gc.set_debug(gc.DEBUG_COLLECTABLE | gc.DEBUG_UNCOLLECTABLE | /    gc.DEBUG_INSTANCES | gc.DEBUG_OBJECTS)  print 'begin leak test...'  make_circle_ref()  print 'begin collect...'  _unreachable = gc.collect()  print 'unreachable object num:%d' % _unreachable  print 'garbage object num:%d' % len(gc.garbage)if __name__ == '__main__':  test_gcleak()

在 test_gcleak() 中,設置垃圾回收器調試標志后,再用 collect() 進行垃圾回收,最后打印出該次垃圾回收發現的不可達的垃圾對象數和整個解釋器中的垃圾對象數。

gc.garbage 是一個 list 對象,列表項是垃圾收集器發現的不可達(即是垃圾對象)、但又不能釋放(即不能回收)的對象。文檔描述為:A list of objects which the collector found to be unreachable but could not be freed (uncollectable objects).
通常,gc.garbage 中的對象是引用環中的對象。因為 Python 不知道按照什么樣的安全次序來調用環中對象的 __del__() 函數,導致對象始終存活在 gc.garbage 中,造成內存泄漏。如果知道一個安全的次序,那么就打破引用環,再執行 del gc.garbage[:] ,以清空垃圾對象列表。

上段代碼輸出為(#后字符串為筆者所加注釋):

#-----------------------------------------begin leak test...# 變量 _gcleak 的引用計數為 2._gcleak ref count0:2# _gcleak 變為不可達(unreachable)的非法變量._gcleak is invalid!# 開始垃圾回收begin collect...# 本次垃圾回收發現的不可達的垃圾對象數為 0.unreachable object num:0# 整個解釋器中的垃圾對象數為 0.garbage object num:0#-----------------------------------------

由此可見 _gcleak 對象的引用計數是正確的,也沒有任何對象發生內存泄漏。

如果不注釋掉 make_circle_ref() 中的 test_code_1 語句:

_gcleak._self = _gcleak

也就是讓 _gcleak 形成一個自己對自己的循環引用。再運行上述代碼,輸出結果就變成:

#-----------------------------------------begin leak test..._gcleak ref count0:3_gcleak is invalid!begin collect...# 發現可以回收的垃圾對象: 地址為 012AA090,類型為 CGcLeak.gc: uncollectable <CGcLeak 012AA090>gc: uncollectable <dict 012AC1E0>unreachable object num:2#!! 不能回收的垃圾對象數為 1,導致內存泄漏!garbage object num:1#-----------------------------------------

可見 <CGcLeak 012AA090> 對象發生了內存泄漏!!而多出的 dict 垃圾就是泄漏的 _gcleak 對象的字典,打印出字典信息為:

{'_self': <__main__.CGcLeak object at 0x012AA090>, '_text': '##########'}

除了對自己的循環引用,多個對象間的循環引用也會導致內存泄漏。簡單舉例如下:

#--------------- code begin --------------class CGcLeakA(object):  def __init__(self):    self._text = '#'*10  def __del__(self):    passclass CGcLeakB(object):  def __init__(self):    self._text = '*'*10  def __del__(self):    passdef make_circle_ref():  _a = CGcLeakA()  _b = CGcLeakB()  _a._b = _b # test_code_2  _b._a = _a # test_code_3  print 'ref count0:a=%d b=%d' % /    (sys.getrefcount(_a), sys.getrefcount(_b))#  _b._a = None  # test_code_4  del _a  del _b  try:    print 'ref count1:a=%d' % sys.getrefcount(_a)  except UnboundLocalError:    print '_a is invalid!'  try:    print 'ref count2:b=%d' % sys.getrefcount(_b)  except UnboundLocalError:    print '_b is invalid!'#--------------- code end ----------------

這次測試后輸出結果為:

#-----------------------------------------begin leak test...ref count0:a=3 b=3_a is invalid!_b is invalid!begin collect...gc: uncollectable <CGcLeakA 012AA110>gc: uncollectable <CGcLeakB 012AA0B0>gc: uncollectable <dict 012AC1E0>gc: uncollectable <dict 012AC0C0>unreachable object num:4garbage object num:2#-----------------------------------------

可見 _a,_b 對象都發生了內存泄漏。因為二者是循環引用,垃圾回收器不知道該如何回收,也就是不知道該首先調用那個對象的 __del__() 函數。

采用以下任一方法,打破環狀引用,就可以避免內存泄漏:

1.注釋掉 make_circle_ref() 中的 test_code_2 語句;
2.注釋掉 make_circle_ref() 中的 test_code_3 語句;
3.取消對 make_circle_ref() 中的 test_code_4 語句的注釋。

相應輸出結果變為:

#-----------------------------------------begin leak test...ref count0:a=2 b=3 # 注:此處輸出結果視情況變化._a is invalid!_b is invalid!begin collect...unreachable object num:0garbage object num:0#-----------------------------------------

結論:Python 的 gc 有比較強的功能,比如設置 gc.set_debug(gc.DEBUG_LEAK) 就可以進行循環引用導致的內存泄露的檢查。如果在開發時進行內存泄露檢查;在發布時能夠確保不會內存泄露,那么就可以延長 Python 的垃圾回收時間間隔、甚至主動關閉垃圾回收機制,從而提高運行效率。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 磴口县| 清新县| 长春市| 龙里县| 武城县| 白河县| 班玛县| 安庆市| 清远市| 大埔县| 长武县| 辽宁省| 江永县| 夏津县| 固阳县| 衡阳市| 晋江市| 界首市| 高雄市| 雷州市| 东阿县| 铜梁县| 蚌埠市| 炉霍县| 饶阳县| 茌平县| 明溪县| 宜宾市| 邵阳县| 桐城市| 怀仁县| 无棣县| 柏乡县| 观塘区| 关岭| 佛教| 建湖县| 越西县| 靖远县| 罗源县| 梁山县|