V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
nthhdy
V2EX  ›  Python

一个用 heapy 进行 Python 程序内存调试的问题

  •  
  •   nthhdy ·
    workingenius · 2017-08-02 16:12:50 +08:00 · 2909 次点击
    这是一个创建于 2669 天前的主题,其中的信息可能已经有所发展或是发生改变。

    本程序现状如下:

    (Pdb) h0 = hp.heap()
    (Pdb) h0
    Partition of a set of 4254865 objects. Total size = 700955416 bytes.
     Index  Count   %     Size   % Cumulative  % Kind (class / dict of class)
         0 646764  15 427504800  61 427504800  61 dict (no owner)
         1 3073581  72 210210328  30 637715128  91 unicode
         2 120775   3 17186360   2 654901488  93 list
         3  71772   2 10795432   2 665696920  95 str
         4  37269   1 10594328   2 676291248  96 _sre.SRE_Pattern
         5 124398   3 10406784   1 686698032  98 tuple
         6    721   0  2114200   0 688812232  98 dict of module
         7  83485   2  2003640   0 690815872  99 int
         8  11706   0  1498368   0 692314240  99 types.CodeType
         9  11157   0  1338840   0 693653080  99 function
    <653 more rows. Type e.g. '_.more' to view.>
    

    可以看到,dict 占用了大部分内存。然而这道这点儿当然不够,我还要知道“哪些字典”大,比如,哪个模块里创建的,怎么引用到它,之类问题。

    难处就在于,这个情景下,嵌套的字典非常多,给一段代码模拟这个场景:

    def rand():
        return random.random()
    
    
    def rand_int(i):
        return random.randint(0, i)
    
    
    def rdt(max_depth, max_width):
        r = {}
    
        if max_depth <= 1:
            for i in range(rand_int(max_width)):
                r[rand()] = rand()
        else:
            for i in range(rand_int(max_width)):
                r[rand()] = rdt(rand_int(max_depth) - 1, max_width)
    
        return r
    
    
    t0 = rdt(9, 9)
    t1 = rdt(7, 14)
    t2 = rdt(5, 19)
    t3 = rdt(3, 24)
    

    现在,请问我直接创建的四个 t,哪个大?

    “大”当然不是指那一个 PyObject 占用的空间大就行了,而是所有直接间接 referents dicts 都加在一起,才有意义。

    所以类似这样的解答不行:

    >>> (h0[0] - h0[0].referents).byvia
    Partition of a set of 5 objects. Total size = 2168 bytes.
     Index  Count   %     Size   % Cumulative  % Referred Via:
         0      1  20     1048  48      1048  48 "['t4']"
         1      1  20      280  13      1328  61 "['t0']"
         2      1  20      280  13      1608  74 "['t1']"
         3      1  20      280  13      1888  87 "['t2']"
         4      1  20      280  13      2168 100 "['t3']"
    

    各位牛人,请教了

    3 条回复    2017-08-03 15:46:26 +08:00
    binfengxy
        1
    binfengxy  
       2017-08-02 16:43:43 +08:00
    这个问题我只能参观了!
    NoAnyLove
        2
    NoAnyLove  
       2017-08-02 23:03:32 +08:00
    以前从来没有遇到过这种问题。提供一个思路,你需要 Pympler,每个变量创建前后调用 tracker.SummaryTracker,然后根据变化量就能知道内存占用了。

    当然,更直观的方法就是 asizeof

    ```
    >>> from pympler import asizeof
    >>> asizeof.asizeof(t0)
    4880
    ```
    nthhdy
        3
    nthhdy  
    OP
       2017-08-03 15:46:26 +08:00
    多谢 pympler 这个工具推荐
    我搜 python memory profile and debug,为什么没搜出这个工具呢,只看到了 line profiler,heapy 啥的

    这样搞是一个思路了,很笨的一个思路呵呵,很慢,跑十几分钟才跑出十分之一:

    ```python
    from pympler.asizeof import asizeof

    h0 = hp.heap()

    ds = h0[0]

    # 获取"根"dict set
    root_ds = (ds - ds.referrers).byid

    # 看一看每个“根 dict ”的 recursive size
    sizes = []
    for i in range(len(root_ds)):
    info = i, asizeof(root_ds[i])
    sizes.append(info)

    # check size
    ```
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1643 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 16:54 · PVG 00:54 · LAX 08:54 · JFK 11:54
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.