python 内存泄露问题定位

  1. memory_profiler是干嘛的
    This is a python module for monitoring memory consumption of a process as well as line-by-line analysis of memory consumption for python programs. It is a pure python module and has the psutil module as optional (but highly recommended) dependencies.

memory_profiler是监控python进程的神器,它可以分析出每一行代码所增减的内存状况。

回到顶部

  1. 入门例子

del3.py

复制代码
import time
@profile
def my_func():
a = [1] * (10 ** 6)
b = [2] * (2 * 10 ** 7)
time.sleep(10)
del b
del a
print "+++++++++"

if name == 'main':
my_func()
复制代码
结果

复制代码
$python -m memory_profiler del3.py
+++++++++
Filename: del3.py

Line # Mem usage Increment Line Contents

 2   10.293 MiB    0.000 MiB   @profile
 3                             def my_func():
 4   17.934 MiB    7.641 MiB       a = [1] * (10 ** 6)
 5  170.523 MiB  152.590 MiB       b = [2] * (2 * 10 ** 7)
 6  170.527 MiB    0.004 MiB       time.sleep(10)
 7   17.938 MiB -152.590 MiB       del b
 8   10.305 MiB   -7.633 MiB       del a
 9   10.309 MiB    0.004 MiB       print "+++++++++"

复制代码
代码执行一遍,然后给出具体代码在某一步占用的内存,通过内存加减可以看出某个对象的大小。

回到顶部
2. 对象不删除,直接赋值内存是否会继续增长

对比1

复制代码
@profile
def my_func():
a = 'a' * 1024 * 1024 * 1024;
a = 'a' * 1024 * 1024
a = 'a' * 1024
del a
print "+++++++++"

if name == 'main':
my_func()
复制代码
结果

复制代码
Line # Mem usage Increment Line Contents

 1   10.293 MiB    0.000 MiB   @profile
 2                             def my_func():
 3 1034.301 MiB 1024.008 MiB       a = 'a' * 1024 * 1024 * 1024;
 4   11.285 MiB -1023.016 MiB       a = 'a' * 1024 * 1024
 5   11.285 MiB    0.000 MiB       a = 'a' * 1024
 6   11.285 MiB    0.000 MiB       del a
 7   11.289 MiB    0.004 MiB       print "+++++++++"

复制代码

对比2

复制代码
@profile
def my_func():
a = 'a' * 1024 * 1024 * 1024;
del a
a = 'a' * 1024 * 1024
del a
a = 'a' * 1024
del a
print "+++++++++"

if name == 'main':
my_func()
复制代码
结果

复制代码
Line # Mem usage Increment Line Contents

 1   10.293 MiB    0.000 MiB   @profile
 2                             def my_func():
 3 1034.301 MiB 1024.008 MiB       a = 'a' * 1024 * 1024 * 1024;
 4   10.297 MiB -1024.004 MiB       del a
 5   11.285 MiB    0.988 MiB       a = 'a' * 1024 * 1024
 6   11.285 MiB    0.000 MiB       del a
 7   11.285 MiB    0.000 MiB       a = 'a' * 1024
 8   11.285 MiB    0.000 MiB       del a
 9   11.289 MiB    0.004 MiB       print "+++++++++"

复制代码
结论:是否 del对象没有影响,新赋的值会替代旧的值

回到顶部
3. 对象赋值是否会增加同样的内存

对比1

复制代码
@profile
def my_func():
a = 'a' * 1024 * 1024 * 1024;
b = a
del a
print "+++++++++"

if name == 'main':
my_func()
复制代码
结果

复制代码
Line # Mem usage Increment Line Contents

 1   10.293 MiB    0.000 MiB   @profile
 2                             def my_func():
 3 1034.301 MiB 1024.008 MiB       a = 'a' * 1024 * 1024 * 1024;
 4 1034.301 MiB    0.000 MiB       b = a
 5 1034.301 MiB    0.000 MiB       del a
 6 1034.305 MiB    0.004 MiB       print "+++++++++"

复制代码

对比2

复制代码
@profile
def my_func():
a = 'a' * 1024 * 1024 * 1024;
b = a
del a
del b
print "+++++++++"

if name == 'main':
my_func()
复制代码
结果

复制代码
Line # Mem usage Increment Line Contents

 1   10.297 MiB    0.000 MiB   @profile
 2                             def my_func():
 3 1034.305 MiB 1024.008 MiB       a = 'a' * 1024 * 1024 * 1024;
 4 1034.305 MiB    0.000 MiB       b = a
 5 1034.305 MiB    0.000 MiB       del a
 6   10.301 MiB -1024.004 MiB       del b
 7   10.305 MiB    0.004 MiB       print "+++++++++"

复制代码
结论,把a赋值给b,内存没有增加。但是只删除其中一个对象的时候,内存不会减。

回到顶部
4. 另一种等价的启动方式
复制代码
from memory_profiler import profile
@profile(precision=4)
def my_func():
a = 'a' * 1024 * 1024 * 1024;
del a
a = 'a' * 1024 * 1024
del a
a = 'a' * 1024
del a
print "+++++++++"

if name == 'main':
my_func()
复制代码
结果

复制代码
$python -m memory_profiler del3.py
+++++++++
Filename: del3.py

Line # Mem usage Increment Line Contents

 2  10.3867 MiB   0.0000 MiB   @profile(precision=4)
 3                             def my_func():
 4 1034.3945 MiB 1024.0078 MiB       a = 'a' * 1024 * 1024 * 1024;
 5  10.3906 MiB -1024.0039 MiB       del a
 6  11.3789 MiB   0.9883 MiB       a = 'a' * 1024 * 1024
 7  11.3789 MiB   0.0000 MiB       del a
 8  11.3789 MiB   0.0000 MiB       a = 'a' * 1024
 9  11.3789 MiB   0.0000 MiB       del a
10  11.3828 MiB   0.0039 MiB       print "+++++++++"

复制代码
回到顶部
5. 非python内置对象例子
复制代码
from memory_profiler import profile
import networkx as nx

@profile(precision=4)
def my_func():
a = 'a' * 1024 * 1024 * 1024;
del a
G = nx.Graph()
G.add_node(1)
G.add_nodes_from([i for i in range(10000)])
G.add_nodes_from([i for i in range(10000, 20000)])
G.add_edges_from([(1,2), (1,4), (2, 9), (4, 1), (3, 8)])
del G
print "++++++"

if name == 'main':
my_func()
复制代码
结果

复制代码
$python del3.py
++++++
Filename: del3.py

Line # Mem usage Increment Line Contents

 4  23.4844 MiB   0.0000 MiB   @profile(precision=4)
 5                             def my_func():
 6 1047.4922 MiB 1024.0078 MiB       a = 'a' * 1024 * 1024 * 1024;
 7  23.4883 MiB -1024.0039 MiB       del a 
 8  23.4883 MiB   0.0000 MiB       G = nx.Graph()
 9  23.4883 MiB   0.0000 MiB       G.add_node(1)
10  31.3359 MiB   7.8477 MiB       G.add_nodes_from([i for i in range(10000)]) 
11  36.9219 MiB   5.5859 MiB       G.add_nodes_from([i for i in range(10000, 20000)]) 
12  36.9219 MiB   0.0000 MiB       G.add_edges_from([(1,2), (1,4), (2, 9), (4, 1), (3, 8)])
13  25.9219 MiB -11.0000 MiB       del G
14  25.9258 MiB   0.0039 MiB       print "++++++"

复制代码
回到顶部
6. 类怎么使用呢

del4.py

复制代码
from memory_profiler import profile

class people:
name = ''
age = 0
__weight = 0

def __init__(self,n,a,w):
    self.name = n
    self.age = a
    self.__weight = w

@profile(precision=4)
def speak(self):
    a = 'a' * 1024
    b = 'b' * 1024 * 1024
    print("%s is speaking: I am %d years old" % (self.name,self.age))

if name == 'main':
p = people('tom', 10, 30)
p.speak()
复制代码
结果

复制代码
$python del4.py
tom is speaking: I am 10 years old
Filename: del4.py

Line # Mem usage Increment Line Contents

13   9.4219 MiB   0.0000 MiB       @profile(precision=4)
14                                 def speak(self):  
15   9.4258 MiB   0.0039 MiB           a = 'a' * 1024
16  10.4297 MiB   1.0039 MiB           b = 'b' * 1024 * 1024
17  10.4336 MiB   0.0039 MiB           print("%s is speaking: I am %d years old" % (self.name,self.age)) 

复制代码
回到顶部
7. 随时间内存统计

test.py

复制代码
import time

@profile
def test1():
n = 10000
a = [1] * n
time.sleep(1)
return a

@profile
def test2():
n = 100000
b = [1] * n
time.sleep(1)
return b

if name == "main":
test1()
test2()
复制代码
test.py 里有两个两个待分析的函数(@profile标识),为了形象地看出内存随时间的变化,每个函数内sleep 1s,执行

mprof run test.py
如果执行成功,结果这样

$ mprof run test.py
mprof: Sampling memory every 0.1s
running as a Python program...
结果会生成一个.dat文件,如"mprofile_20160716170529.dat",里面记录了内存随时间的变化,可用下面的命令以图片的形式展示出来:

mprof plot

回到顶部
8. API
memory_profiler提供很多包给第三方代码,如

from memory_profiler import memory_usage
mem_usage = memory_usage(-1, interval=.2, timeout=1)
print(mem_usage)
[7.296875, 7.296875, 7.296875, 7.296875, 7.296875]
memory_usage(proc=-1, interval=.2, timeout=None)返回一段时间的内存值,其中proc=-1表示此进程,这里可以指定特定的进程号;interval=.2表示监控的时间间隔是0.2秒;timeout=1表示总共的时间段为1秒。那结果就返回5个值。

如果要返回一个函数的内存消耗,示例

复制代码
def f(a, n=100):
import time
time.sleep(2)
b = [a] * n
time.sleep(1)
return b

from memory_profiler import memory_usage
print memory_usage((f, (2,), {'n' : int(1e6)}))
复制代码
这里执行了 f(1, n=int(1e6)) ,并返回在执行此函数时的内存消耗。

回到顶部
9. 优化实例
对比str & int

复制代码
from datetime import datetime
@profile
def my_func():
beg = datetime.now()
a = {}
for i in range(1000000):
a[i] = i
#a[str(i)] = i
print "+++++++++"
del a
print "+++++++++"
end = datetime.now()
print "time:", end - beg

if name == 'main':
my_func()
复制代码
用a[i] = i,结果

复制代码
+++++++++
+++++++++
time: 0:06:14.790899
Filename: int.py

Line # Mem usage Increment Line Contents

 2   14.727 MiB    0.000 MiB   @profile
 3                             def my_func():
 4   14.734 MiB    0.008 MiB       beg = datetime.now()
 5   14.734 MiB    0.000 MiB       a = {}
 6   94.031 MiB   79.297 MiB       for i in range(1000000):
 7   94.031 MiB    0.000 MiB           a[i] = i
 8                                     #a[str(i)] = i
 9   86.402 MiB   -7.629 MiB       print "+++++++++"
10   38.398 MiB  -48.004 MiB       del a
11   38.398 MiB    0.000 MiB       print "+++++++++"
12   38.398 MiB    0.000 MiB       end = datetime.now()
13   38.406 MiB    0.008 MiB       print "time:", end - beg

复制代码
用a[str(i)] = i,结果

复制代码
+++++++++
+++++++++
time: 0:06:00.288052
Filename: int.py

Line # Mem usage Increment Line Contents

 2   14.723 MiB    0.000 MiB   @profile
 3                             def my_func():
 4   14.730 MiB    0.008 MiB       beg = datetime.now()
 5   14.730 MiB    0.000 MiB       a = {}
 6  140.500 MiB  125.770 MiB       for i in range(1000000):
 7                                     #a[i] = i
 8  140.500 MiB    0.000 MiB           a[str(i)] = i
 9  132.871 MiB   -7.629 MiB       print "+++++++++"
10   38.539 MiB  -94.332 MiB       del a
11   38.539 MiB    0.000 MiB       print "+++++++++"
12   38.539 MiB    0.000 MiB       end = datetime.now()
13   38.547 MiB    0.008 MiB       print "time:", end - beg

复制代码

https://www.cnblogs.com/kaituorensheng/p/5669861.html

posted @ 2021-07-07 15:44  哈哈哈喽喽喽  阅读(213)  评论(0编辑  收藏  举报