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
yanyuechuixue
V2EX  ›  Python

想问一下,为什么这段代码内存占用不会降下来?

  •  
  •   yanyuechuixue · 2016-03-22 20:42:19 +08:00 · 3116 次点击
    这是一个创建于 3168 天前的主题,其中的信息可能已经有所发展或是发生改变。

    这是一部分代码,全部代码的话太长了。

        def main(rg1,rg2):
        global Abins
        Abins=[[[] for x in range(50)] for y in range(50)]
        for ra in range(rg1,rg2):
            for dec in range(180):
                print ra, dec
                if file_list[ra][dec] == []:
                    continue
                else:
                    print len(file_list[ra][dec])
                    for b in file_list[ra][dec]:
                        for bins_ra in range(-4,5):
                            for bins_dec in range(-4,5):
                                for a in file_list[ra+bins_ra][dec+bins_dec]:
                                    if a[4]<b[4]:
                                        calcute(str(a[0]),str(b[0]),a[3],b[3],a[1],b[1],a[2],b[2])
        print("start print")
        for i in range(FenShu):
            for j in range(FenShu):
                file_save=open('./Abins_'+str(rg1)+'_'+str(rg2)+'_'+str(i)+'_'+str(j), 'w')
                file_save.write(json.dumps(Abins[i][j]))
                file_save.close()
        del Abins
        gc.collect()
    
    
    for ra in range(80,140):
        main(ra,ra+10)
    

    这段代码的意思是,我每做一次 main 这个函数,都会往全局变量 Abins 数组中添加一些数据,由于全部做完之后这个数组会太大(远超过物理内存),所以每一次循环都输出一个文件。

    但我的意图是输出完这个文件之后应该就释放了原来的 Abins 专用的内存了,不知道为什么执行的时候还是占用很多很多内存。 是做天文数据处理用的。 多谢!

    22 条回复    2016-03-25 21:06:18 +08:00
    justfly
        1
    justfly  
       2016-03-22 20:48:57 +08:00
    一个 python 进程内存的占用是这个进程运行中峰值的内存占用。

    python 会把回收的内存放到自己的空闲链表中,并不是还给操作系统。
    yanyuechuixue
        2
    yanyuechuixue  
    OP
       2016-03-22 20:53:23 +08:00
    @justfly 这样的话还是会跑着跑着内存占用 100%,然后跑不动了啊。。
    有什么办法可以把内存占用降下来么?
    yanyuechuixue
        3
    yanyuechuixue  
    OP
       2016-03-22 20:54:24 +08:00
    @justfly 就是,当内存占用百分之九十多的时候, CPU 占用率就只有百分之四、五, 而不是百分之百了……所以我认为是跑不动了。
    justfly
        4
    justfly  
       2016-03-22 21:04:49 +08:00
    函数里面的变量没必要 global 。 先看看你一次 main 占多少内存吧。把 for 去掉 只跑一次 main 然后用 sleep block 住看内存。
    northisland
        5
    northisland  
       2016-03-22 21:07:02 +08:00   ❤️ 1
    del Abins
    这句放在 2 重循环外边了吧~~
    读完所有文件再释放?
    yanyuechuixue
        6
    yanyuechuixue  
    OP
       2016-03-22 21:12:26 +08:00
    @justfly 因为如果不 global 的话,在这个函数调用的另一个子函数就没法往 Abins 里填充元素……
    yanyuechuixue
        7
    yanyuechuixue  
    OP
       2016-03-22 21:19:37 +08:00
    @northisland 嗯,执行完 main()之后再释放,下一次执行 main()再创建。
    dreampuf
        8
    dreampuf  
       2016-03-22 21:24:20 +08:00   ❤️ 1
    CPython 不会将内存释放给操作系统。 GC 只会将对象回收到 runtime 的 free list 中,以备再用。
    解决方案简单粗暴就用子进程进行数据处理,然后将结果传递给父进程。
    yanyuechuixue
        9
    yanyuechuixue  
    OP
       2016-03-22 21:29:32 +08:00
    @dreampuf 好的,谢谢,我试一下。
    麻烦问一下,如果传递一个很大的数组的话,会不会速度会很慢?
    dreampuf
        10
    dreampuf  
       2016-03-22 21:34:33 +08:00
    如何传递?跨进程 /线程,还是同上下文只是函数调用?
    建议 Google 一下,这些都是书本上的知识了
    cphilo
        11
    cphilo  
       2016-03-22 21:36:25 +08:00
    考虑使用类似 xrange 这种迭代器
    cphilo
        12
    cphilo  
       2016-03-22 21:38:17 +08:00
    应该是生成器,不是迭代器, v2 不能删除评论
    sujin190
        13
    sujin190  
       2016-03-22 21:42:28 +08:00
    @dreampuf free list 不是有长度限制的么?超过了依然会释放的,不过会有整数缓存,字符串缓存什么的,如果你从 i 循环到数百万,内存占用数百 M 是正常的
    mko0okmko0
        14
    mko0okmko0  
       2016-03-22 22:15:50 +08:00
    PYPY 能好一点吗?我不知道等楼下回答.

    我个人会推荐换用 JAVA 或其他语言来解决脚本语言的性能或是回收问题.
    我帮你找的方法:
    http://f.dataguru.cn/thread-296696-1-1.html

    个人觉得 C#(mono)很棒(有 SIMD 加速),JAVA 次之(有 SSE/AVX 加速但要加参数)
    dreampuf
        15
    dreampuf  
       2016-03-22 22:30:25 +08:00
    sujin190
        16
    sujin190  
       2016-03-23 13:21:21 +08:00
    @dreampuf 是的,一般不开启,但看这个逻辑似乎是每次最小分配 16*256KB 字节,再分成 256KB 大小的每块,再在每块上分配对象需要的内存,但一块上所有对象都被释放的话,该块还是会被释放的
    dreampuf
        17
    dreampuf  
       2016-03-23 18:19:32 +08:00
    @sujin190 哪段代码?
    sujin190
        18
    sujin190  
       2016-03-23 20:41:06 +08:00
    @dreampuf https://github.com/python/cpython/blob/master/Objects/obmalloc.c#L1598
    而且内存限制也不是说不能超过这个内存,而是当内存超过这个值之后直接由 c 的 malloc 直接来分配,所以如果你有引用没有释放,内存依然还是会占用很高的
    billgreen1
        19
    billgreen1  
       2016-03-24 00:10:51 +08:00
    强烈建议你重构代码,六个 for 循环,我从来没见到过这么多的循环嵌套的。
    yanyuechuixue
        20
    yanyuechuixue  
    OP
       2016-03-25 20:14:47 +08:00
    @billgreen1 我也没见过……这个已经没办法的事情了……这是我能想到的最节约时间数据结构决定的……
    是把整个天空分成 180*360 个格子,每个里面放了好多个数组,对每个数组都要与周围的 64 个格子里的其他数组进行运算,这个运算包含两个循环………………
    billgreen1
        21
    billgreen1  
       2016-03-25 21:03:45 +08:00
    @yanyuechuixue

    那就拆分,用 generator 。

    假设天空 180*360 的格子 命名为 grids.

    paired_array = ( (array1, array2)
    for array1 in grid
    for array2 in neighbor
    for neighbor in get_neighbors(grid)
    for grid in grids
    )

    result = [calculate(array1, array2) for array1, array2 in paired_array]
    billgreen1
        22
    billgreen1  
       2016-03-25 21:06:18 +08:00
    我猜你的 calculate 运算时对称的, calculate(x,y) 等于 calculate(y,x)
    你可以试试 lru.cache 来节约运算时间。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   999 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 19ms · UTC 19:40 · PVG 03:40 · LAX 11:40 · JFK 14:40
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.