亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

Python的內(nèi)存泄漏及gc模塊的使用分析

系統(tǒng) 1759 0

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

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__() 函數(shù)的對(duì)象間的循環(huán)引用是導(dǎo)致內(nèi)存泄漏的主兇
另外需要說明: 對(duì)沒有 __del__() 函數(shù)的 Python 對(duì)象間的循環(huán)引用,是可以被自動(dòng)垃圾回收掉的

如何知道一個(gè)對(duì)象是否內(nèi)存泄漏了呢?

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

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


首先,來看一段正常的測(cè)試代碼:

            
#--------------- code begin --------------
# -*- coding: utf-8 -*-
import gc
import sys

class CGcLeak(object):
  def __init__(self):
    self._text = '#'*10

  def __del__(self):
    pass

def 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() 中,設(shè)置垃圾回收器調(diào)試標(biāo)志后,再用 collect() 進(jìn)行垃圾回收,最后打印出該次垃圾回收發(fā)現(xiàn)的不可達(dá)的垃圾對(duì)象數(shù)和整個(gè)解釋器中的垃圾對(duì)象數(shù)。

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

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

            
#-----------------------------------------
begin leak test...
# 變量 _gcleak 的引用計(jì)數(shù)為 2.
_gcleak ref count0:2
# _gcleak 變?yōu)椴豢蛇_(dá)(unreachable)的非法變量.
_gcleak is invalid!
# 開始垃圾回收
begin collect...
# 本次垃圾回收發(fā)現(xiàn)的不可達(dá)的垃圾對(duì)象數(shù)為 0.
unreachable object num:0
# 整個(gè)解釋器中的垃圾對(duì)象數(shù)為 0.
garbage object num:0
#-----------------------------------------


          

由此可見 _gcleak 對(duì)象的引用計(jì)數(shù)是正確的,也沒有任何對(duì)象發(fā)生內(nèi)存泄漏。

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

            
_gcleak._self = _gcleak


          

也就是讓 _gcleak 形成一個(gè)自己對(duì)自己的循環(huán)引用。再運(yùn)行上述代碼,輸出結(jié)果就變成:

            
#-----------------------------------------
begin leak test...
_gcleak ref count0:3
_gcleak is invalid!
begin collect...
# 發(fā)現(xiàn)可以回收的垃圾對(duì)象: 地址為 012AA090,類型為 CGcLeak.
gc: uncollectable 
            
              
gc: uncollectable 
              
                
unreachable object num:2
#!! 不能回收的垃圾對(duì)象數(shù)為 1,導(dǎo)致內(nèi)存泄漏!
garbage object num:1
#-----------------------------------------


              
            
          

可見 對(duì)象發(fā)生了內(nèi)存泄漏!!而多出的 dict 垃圾就是泄漏的 _gcleak 對(duì)象的字典,打印出字典信息為:

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


          

除了對(duì)自己的循環(huán)引用,多個(gè)對(duì)象間的循環(huán)引用也會(huì)導(dǎo)致內(nèi)存泄漏。簡(jiǎn)單舉例如下:

            
#--------------- code begin --------------

class CGcLeakA(object):
  def __init__(self):
    self._text = '#'*10

  def __del__(self):
    pass

class CGcLeakB(object):
  def __init__(self):
    self._text = '*'*10

  def __del__(self):
    pass

def 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 ----------------


          

這次測(cè)試后輸出結(jié)果為:

            
#-----------------------------------------
begin leak test...
ref count0:a=3 b=3
_a is invalid!
_b is invalid!
begin collect...
gc: uncollectable 
            
              
gc: uncollectable 
              
                
gc: uncollectable 
                
                  
gc: uncollectable 
                  
                    
unreachable object num:4
garbage object num:2
#-----------------------------------------

                  
                
              
            
          

可見 _a,_b 對(duì)象都發(fā)生了內(nèi)存泄漏。因?yàn)槎呤茄h(huán)引用,垃圾回收器不知道該如何回收,也就是不知道該首先調(diào)用那個(gè)對(duì)象的 __del__() 函數(shù)。

采用以下任一方法,打破環(huán)狀引用,就可以避免內(nèi)存泄漏:

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

相應(yīng)輸出結(jié)果變?yōu)椋?

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


          

結(jié)論:Python 的 gc 有比較強(qiáng)的功能,比如設(shè)置 gc.set_debug(gc.DEBUG_LEAK) 就可以進(jìn)行循環(huán)引用導(dǎo)致的內(nèi)存泄露的檢查。如果在開發(fā)時(shí)進(jìn)行內(nèi)存泄露檢查;在發(fā)布時(shí)能夠確保不會(huì)內(nèi)存泄露,那么就可以延長(zhǎng) Python 的垃圾回收時(shí)間間隔、甚至主動(dòng)關(guān)閉垃圾回收機(jī)制,從而提高運(yùn)行效率。


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 手机福利在线观看 | 成人午夜| 免费一级毛片视频 | 久久草在线观看 | 国产欧美一区二区精品久久久 | 亚洲欧美日韩精品久久 | 日韩中文欧美 | 91视频网址入口 | 一区二区三区在线免费视频 | 国产成人毛片亚洲精品不卡 | 国产美女久久 | 色综合亚洲综合网站综合色 | 干亚洲美女 | 天天做天天做天天综合网 | 国产香蕉视频在线观看 | 国产精品久久久久国产精品三级 | 真人一级一级特黄高清毛片 | 国产精品福利视频手机免费观看 | 97天天做天天爱夜夜爽 | 日韩伦理一区二区 | 欧美理论在线观看 | 波多野吉衣一区二区三区在线观看 | 免费视频国产 | 国产欧美一区二区三区观看 | 日本高中生一级乇片 | 国产欧美在线观看精品一区二区 | 波多野结衣久久 | 日韩不卡毛片 | 色综合亚洲七七久久桃花影院 | 国产成人一区二区三区免费观看 | 日韩字幕一中文在线综合 | 国产亚洲精品国产第一 | 欧美一区二区三区综合色视频 | 亚洲免费小视频 | 色婷婷精品大全在线视频 | 99r在线| 久草视频在线观 | 国产精品第一区亚洲精品 | 久久久久亚洲精品一区二区三区 | 久久黄色免费视频 | 日本欧美高清视频 |