<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    常用鏈接

    統(tǒng)計(jì)

    最新評論

    Python 循環(huán)引用導(dǎo)致內(nèi)存泄漏的內(nèi)存管理TEST ref from:http://meizhini.javaeye.com/blog/249716

      在 Python 中,為了解決內(nèi)存泄漏問題,采用了對象引用計(jì)數(shù),并基于引用計(jì)數(shù)實(shí)現(xiàn)自動(dòng)垃圾回收。
        因?yàn)?Python 有了自動(dòng)垃圾回收功能,不少初學(xué)者就認(rèn)為自己從此過上了好日子,不必再受內(nèi)存泄漏的騷擾了。但如果查看一下 Python 文檔對 __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ù)的對象間的循環(huán)引用是導(dǎo)致內(nèi)存泄漏的主兇。特別說明:對沒有 __del__() 函數(shù)的 Python 對象間的循環(huán)引用,是可以被自動(dòng)垃圾回收掉的。


    有 __del__() 函數(shù)的對象間的循環(huán)引用是導(dǎo)致內(nèi)存泄漏的主兇。特別說明:對沒有 __del__() 函數(shù)的 Python 對象間的循環(huán)引用,是可以被自動(dòng)垃圾回收掉的。所以,沒什么事,千萬不要輕易啟用__del__操作。
    如何知道一個(gè)對象是否內(nèi)存泄漏了呢?
        方法一、當(dāng)你認(rèn)為一個(gè)對象應(yīng)該被銷毀時(shí)(即引用計(jì)數(shù)為 0),可以通過 sys.getrefcount(obj) 來獲取對象的引用計(jì)數(shù),并根據(jù)返回值是否為 0 來判斷是否內(nèi)存泄漏。如果返回的引用計(jì)數(shù)不為 0,說明在此刻對象 obj 是不能被垃圾回收器回收掉的。
        方法二、也可以通過 Python 擴(kuò)展模塊 gc 來查看不能回收的對象的詳細(xì)信息。


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

    #--------------- 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()

    #--------------- code end ----------------

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

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

        上段代碼輸出為(#后字符串為筆者所加注釋):
    #-----------------------------------------
    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á)的垃圾對象數(shù)為 0.
    unreachable object num:0
    # 整個(gè)解釋器中的垃圾對象數(shù)為 0.
    garbage object num:0
    #-----------------------------------------
        可見 _gcleak 對象的引用計(jì)數(shù)是正確的,也沒有任何對象發(fā)生內(nèi)存泄漏。

        如果不注釋掉 make_circle_ref() 中的 test_code_1 語句:
        _gcleak._self = _gcleak
    也就是讓 _gcleak 形成一個(gè)自己對自己的循環(huán)引用。再運(yùn)行上述代碼,輸出結(jié)果就變成:
    #-----------------------------------------
    begin leak test...
    _gcleak ref count0:3
    _gcleak is invalid!
    begin collect...
    # 發(fā)現(xiàn)可以回收的垃圾對象: 地址為 012AA090,類型為 CGcLeak.
    gc: uncollectable <CGcLeak 012AA090>
    gc: uncollectable <dict 012AC1E0>
    unreachable object num:2
    #!! 不能回收的垃圾對象數(shù)為 1,導(dǎo)致內(nèi)存泄漏!
    garbage object num:1
    #-----------------------------------------
        可見 <CGcLeak 012AA090> 對象發(fā)生了內(nèi)存泄漏!!而多出的 dict 垃圾就是泄漏的 _gcleak 對象的字典,打印出字典信息為:
    {'_self': <__main__.CGcLeak object at 0x012AA090>, '_text': '##########'}

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

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

        這次測試后輸出結(jié)果為:
    #-----------------------------------------
    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:4
    garbage object num:2
    #-----------------------------------------
    可見 _a,_b 對象都發(fā)生了內(nèi)存泄漏。因?yàn)槎呤茄h(huán)引用,垃圾回收器不知道該如何回收,也就是不知道該首先調(diào)用那個(gè)對象的 __del__() 函數(shù)。

        采用以下任一方法,打破環(huán)狀引用,就可以避免內(nèi)存泄漏:
    [1] 注釋掉 make_circle_ref() 中的 test_code_2 語句;
    [2] 注釋掉 make_circle_ref() 中的 test_code_3 語句;
    [3] 取消對 make_circle_ref() 中的 test_code_4 語句的注釋。
    相應(yīng)輸出結(jié)果變?yōu)椋?br /> #-----------------------------------------
    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)存泄露,那么就可以延長 Python 的垃圾回收時(shí)間間隔、甚至主動(dòng)關(guān)閉垃圾回收機(jī)制,從而提高運(yùn)行效率。

    posted on 2009-11-30 10:47 九寶 閱讀(1328) 評論(0)  編輯  收藏 所屬分類: Python

    主站蜘蛛池模板: 美女视频黄a视频全免费网站色 | 亚洲国产一区在线观看 | 99久9在线|免费| 亚洲av无码精品网站| 在线观看免费无码专区| 亚洲成av人在线视| 91麻豆国产免费观看| 亚洲精品日韩专区silk| 日本成年免费网站| 亚洲第一成年免费网站| 又爽又高潮的BB视频免费看| 香蕉97碰碰视频免费| 伊伊人成亚洲综合人网7777| 免费视频精品一区二区三区 | 亚洲一久久久久久久久| 国内外成人免费视频| 日韩色日韩视频亚洲网站| 又粗又大又长又爽免费视频| 一级做a爱过程免费视| 国产AV无码专区亚洲AV男同| 一级毛片在线免费看| 亚洲看片无码在线视频| 四虎影视精品永久免费| aaa毛片免费观看| 亚洲精品欧洲精品| 免费v片视频在线观看视频| 三根一起会坏掉的好痛免费三级全黄的视频在线观看 | 午夜免费福利在线| 一区二区3区免费视频| 91在线亚洲精品专区| 浮力影院第一页小视频国产在线观看免费 | 成人男女网18免费视频| 一级毛片大全免费播放下载| 亚洲国产综合91精品麻豆| 97人伦色伦成人免费视频| 一级一黄在线观看视频免费| 亚洲精品国产啊女成拍色拍| 国产成人aaa在线视频免费观看| 岛国精品一区免费视频在线观看 | 国拍在线精品视频免费观看 | 3344在线看片免费|