问题描述
调试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的详细用法,可以看这里(http://smira.ru/wp-content/uploads/2011/08/heapy.html)。
以上这篇对python程序内存泄漏调试的记录就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持服务器之家。
原文链接:https://blog.csdn.net/ybdesire/article/details/73128353