end0tknr's kipple - web写経開発

太宰府天満宮の狛犬って、妙にカワイイ

weakref for python を使用した弱参照化によるメモリリーク回避

「ゼロから作るディープラーニング 3 」の 「ステップ17 メモリ管理と循環参照」において、 weakrefによる弱参照化が紹介されていましたので、メモ

weakref.ref() を使用する場合

参考url https://yumarublog.com/python/weakref/

import sys
import weakref

def main():
    mem_leak_proc()
    no_mem_leak_proc()
    
def mem_leak_proc():
    member1 = Member(1, "tim")
    ref1 = member1  # 強参照 作成
    del member1     # 参照元 object削除

    # 強参照の為、上記でdelしているが、参照先では残ります
    print(sys._getframe().f_code.co_name, ref1.name)

def no_mem_leak_proc():
    member1 = Member(1, "tim")

    # 弱参照 生成
    ref1 = weakref.ref( member1 )
    print(sys._getframe().f_code.co_name, ref1().name)

    # 参照元 object削除
    del member1

    if ref1() == None:
        print(sys._getframe().f_code.co_name, "ref1 not exist")
    else:
        print(sys._getframe().f_code.co_name, "ref1 exists")

class Member:
    def __init__(self, id, name):
        self.id = id
        self.name = name

if __name__ == '__main__':
    main()

↑こう書くと↓こう表示されます

> python foo_2.py
mem_leak_proc tim
no_mem_leak_proc tim
None
no_mem_leak_proc ref1 not exist

weakref.WeakValueDictionary() を使用する場合

参考url https://qiita.com/pashango2/items/fb1e5e79589279c5a861

import sys
import weakref

def main():
    mem_leak_proc()
    no_mem_leak_proc()
    
def mem_leak_proc():
    # idをkeyにした辞書
    members_dict = {
        x.id : x for x in [ Member(1, "tim"),
                            Member(2, "matthew"),
                            Member(3, "Jack") ]
    }
    # 上記を元にnameをkeyにした辞書.(循環参照)
    names_dict = {x.name: x for x in members_dict.values()}

    del members_dict[1]
    # 上記でdelしているが、循環参照により、以下は値が残り、
    # このメモリリーク?を改善するには、del names_dict['tim'] も必要
    name_key = "tim"
    if name_key in names_dict:
        print(sys._getframe().f_code.co_name, name_key,"exists.")
    else:
        print(sys._getframe().f_code.co_name, name_key,"don't exist.")

def no_mem_leak_proc():
    # idをkeyにした辞書
    members_dict = {
        x.id : x for x in [ Member(1, "tim"),
                            Member(2, "matthew"),
                            Member(3, "Jack") ]
    }

    names_dict = weakref.WeakValueDictionary(
        {x.name: x for x in members_dict.values()}
    )
    
    del members_dict[1]
    name_key = "tim"
    if name_key in names_dict:
        print(sys._getframe().f_code.co_name, name_key,"exists.")
    else:
        print(sys._getframe().f_code.co_name, name_key,"don't exist.")

class Member:
    def __init__(self, id, name):
        self.id = id
        self.name = name

if __name__ == '__main__':
    main()

↑こう書くと↓こう表示されます

>python foo.py
mem_leak_proc tim exists.
no_mem_leak_proc tim don't exist.