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

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

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

    常用鏈接

    統計

    最新評論

    Python 循環引用導致內存泄漏的內存管理TEST ref from:http://meizhini.javaeye.com/blog/249716

      在 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 對象間的循環引用,是可以被自動垃圾回收掉的。


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


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

    #--------------- 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() 中,設置垃圾回收器調試標志后,再用 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):
            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 ----------------

        這次測試后輸出結果為:
    #-----------------------------------------
    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 對象都發生了內存泄漏。因為二者是循環引用,垃圾回收器不知道該如何回收,也就是不知道該首先調用那個對象的 __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:0
    garbage object num:0
    #-----------------------------------------


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

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

    主站蜘蛛池模板: 亚洲网红精品大秀在线观看| 亚洲福利精品电影在线观看| 图图资源网亚洲综合网站| 老司机福利在线免费观看| 国产成人涩涩涩视频在线观看免费| 亚洲AV无码专区在线亚| 中文字幕av无码无卡免费| 亚洲午夜国产精品无卡| 国产福利在线免费| 色噜噜亚洲男人的天堂| 免费无码精品黄AV电影| 亚洲最大中文字幕无码网站 | 亚洲av无码日韩av无码网站冲| 啦啦啦手机完整免费高清观看| 亚洲成a人无码亚洲成av无码 | 亚洲欧洲免费无码| 国产一级一片免费播放| xxxxx做受大片视频免费| 亚洲国产a∨无码中文777 | 成人毛片免费网站| 免费无码午夜福利片69| 亚洲熟妇av一区二区三区漫画| 国产一精品一av一免费爽爽| 亚洲电影唐人社一区二区| 99久久免费精品国产72精品九九| 欧洲亚洲国产精华液| 亚洲一区无码精品色| 日韩精品无码免费一区二区三区 | 亚洲AV成人片色在线观看| 国产曰批免费视频播放免费s| 亚洲熟妇无码八V在线播放| 亚洲v国产v天堂a无码久久| 国内精品免费在线观看 | 亚洲一卡二卡三卡四卡无卡麻豆| 国产真人无遮挡作爱免费视频 | 免费国产成人α片| 亚洲精品自偷自拍无码| 国产亚洲日韩在线三区| 韩国免费一级成人毛片| 人体大胆做受免费视频| 亚洲免费在线视频播放|