Chinaunix首页 | 论坛 | 博客
  • 博客访问: 4425397
  • 博文数量: 1214
  • 博客积分: 13195
  • 博客等级: 上将
  • 技术积分: 9105
  • 用 户 组: 普通用户
  • 注册时间: 2007-01-19 14:41
个人简介

C++,python,热爱算法和机器学习

文章分类

全部博文(1214)

文章存档

2021年(13)

2020年(49)

2019年(14)

2018年(27)

2017年(69)

2016年(100)

2015年(106)

2014年(240)

2013年(5)

2012年(193)

2011年(155)

2010年(93)

2009年(62)

2008年(51)

2007年(37)

分类: Python/Ruby

2020-12-01 10:18:26

https://www.cnblogs.com/luoahong/articles/9259063.html

问题描述

调试python程序时,用下面这段代码,可以获得进程占用系统内存值。程序跑一段时间后,就能画出进程对内存的占用情况。

1
2
3
4
5
6
def memory_usage_psutil():
    # return the memory usage in MB
    import psutil,os
    process = psutil.Process(os.getpid())
    mem = process.memory_info()[0] / float(2 ** 20)
    return mem

发现进程的内存占用一直再上涨,而这从逻辑上来说是不正常的,所以想到程序可能发生了Memory Leak。

python程序的Mem Leak

python程序不可能像C/C++一样出现malloc了的内存没有free这样的Memory Leak。但也会遇到“逻辑上没free”的情况,如下代码所示。

1
2
3
def foo(a=[]):
    a.append(time.time())
    return a

参数a这样可迭代的对象,稍不注意,它就能增长的很快。说白了,python的Memory Leak,就是“进程占用的内存莫名其妙一直再升高”。进程占用内存一直升高,与逻辑预期不一致,就可能发生了Memory Leak。

以下面程序为例说明Memory Leak调试的过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def memory_usage_psutil():
    # return the memory usage in MB
    import psutil,os
    process = psutil.Process(os.getpid())
    mem = process.memory_info()[0] / float(2 ** 20)
    return mem
 
def get_current_obj(a=[]):
    a.append([0]*1000)
    return a
 
def main():   
    obj = []
    for i in range(10000):
        obj = get_current_obj(obj)
        if(i%100==0):
            print(memory_usage_psutil())
 
if __name__=='__main__':
    main()

调试过程

用pmap -x [pid]查看进程占用的堆内存大小

首先想到,会不会是上面用的memory_usage_psutil函数统计错误呢。

先运行程序,再用pmap查看,发现进程内存占用确实很高。多次执行该命令,也可以发现内存一直升高。 
这里写图片描述

强制执行GC(gc.collect())

在需要执行GC的地方加上gc.collect()

1
2
3
4
5
6
7
def main():   
    obj = []
    for i in range(10000):
        obj = get_current_obj(obj)
        import gc;gc.collect()
        if(i%100==0):
            print(memory_usage_psutil())

可以看到,强制GC后,程序执行变慢,但内存依然不断升高。

使用memory_profiler查看

  • 安装memory_profiler
1
pip install -U memory_profiler
  • 用@profile修饰需要查看内存的函数
1
2
3
4
5
6
7
@profile
def main():   
    obj = []
    for i in range(10000):
        obj = get_current_obj(obj)
        if(i%100==0):
            print(memory_usage_psutil())
  • 用如下命令运行程序
1
python -m memory_profiler main.py

可以看到程序执行完成后,输出结果如下

1
2
3
4
5
6
7
8
9
Line #    Mem usage    Increment   Line Contents
================================================
    12   28.570 MiB    0.000 MiB   @profile
    13                             def main():
    14   28.570 MiB    0.000 MiB       obj = []
    15  106.203 MiB   77.633 MiB       for i in range(10000):
    16  106.203 MiB    0.000 MiB           obj = get_current_obj(obj)
    17  106.203 MiB    0.000 MiB           if(i%100==0):
    18  105.445 MiB   -0.758 MiB               print(memory_usage_psutil())

这样就能看到导致内存上涨最快的那几行代码。

用guppy查看python对象占用的堆内存大小

将main修改如下,即可查看python对堆内存的占用量。

1
2
3
4
5
6
7
8
def main():   
    obj = []
    for i in range(10000):
        obj = get_current_obj(obj)
        if(i%100==0):
            print(memory_usage_psutil())
            from guppy import hpy;hxx = hpy();heap = hxx.heap()
            print(heap)

下面就是输出结果,python程序中各个对象对内存的占用从大到小排列。

1
2
3
4
5
6
7
8
9
10
11
Index  Count   %     Size   % Cumulative  % Kind (class / dict of class)
    0  10124  22 81944416  95  81944416  95 list
    1  16056  34  1325464   2  83269880  96 str
    2   9147  20   745616   1  84015496  97 tuple
    3    102   0   366480   0  84381976  98 dict of module
    4    287   1   313448   0  84695424  98 dict of type
    5   2426   5   310528   0  85005952  98 types.CodeType
    6   2364   5   283680   0  85289632  99 function
    7    287   1   256960   0  85546592  99 type
    8    169   0   192088   0  85738680  99 dict (no owner)
    9    123   0   142728   0  85881408  99 dict of class

可以从结果中看到,95%的进程内存,都被一个list占用。

还可以通过下面这种方式,查看这个占内存最大的list中的数据类型。

1
from guppy import hpy;hxx = hpy();byrcs = hxx.heap().byrcs; byrcs[0].byid

关于guppy的详细用法,可以看这里()。

参考

  • mem leak in py. 
作者:罗阿红 出处:http://www.cnblogs.com/luoahong/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。
阅读(987) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~