Python 性能分析之内存使用
上一篇文章我们已经掌握了如何查看代码中的时间消耗,今天我们继续讲讲程序中的内存消耗,当然对于python我们也有现成的包Fabian Pedregosa以 Robert Kern 的 line_profiler 为模型,实现了一个很好的内存分析器。
首先:通过pip安装:
$ pip install -U memory_profiler
$ pip install psutil
(建议在此处安装psutil
包,因为它大大提高了 memory_profiler 的性能)。
与 line_profiler 一样,memory_profiler 要求您使用如下装饰器装饰您感兴趣的函数@profile
:
@profile
def primes(n):
...
...
要查看您的函数使用了多少内存,请运行以下命令:
$ python -m memory_profiler primes.py
一旦程序退出,您应该会看到如下所示的输出:
line_profiler 和 memory_profiler 的 IPython 快捷方式
当然line_profiler
,memory_profiler
这两个程序都有从IPython中访问的快捷方式,如下所示,这样就可以节约大量时间和精力,无需修改任何源代码即可分析代码
%load_ext memory_profiler
%load_ext line_profiler
内存泄漏在哪里?
cPython 解释器使用引用计数作为跟踪内存的主要方法。这意味着每个对象都包含一个计数器,当对该对象的引用存储在某处时该计数器递增,并在删除对该对象的引用时递减。当计数器达到零时,cPython 解释器知道该对象不再使用,因此它删除该对象并释放占用的内存。
如果即使对象不再使用,对对象的引用仍然存在,则程序中经常会发生内存泄漏。
找到这些“内存泄漏”的最快方法是使用由 Marius Gedminas 编写的名为objgraph的出色工具。该工具允许您查看内存中对象的数量,并定位代码中保存对这些对象的引用的所有不同位置。
要开始,首先安装objgraph
:
pip install objgraph
安装此工具后,在代码中插入一条语句以调用调试器:
import pdb; pdb.set_trace()
哪些对象最常见?
在运行时,您可以通过运行以下命令检查程序中最流行的 20 个对象:
(pdb) import objgraph
(pdb) objgraph.show_most_common_types()
MyBigFatObject 20000
tuple 16938
function 4310
dict 2790
wrapper_descriptor 1181
builtin_function_or_method 934
weakref 764
list 634
method_descriptor 507
getset_descriptor 451
type 439
添加或删除了哪些对象?
我们还可以看到在两个时间点之间添加或删除了哪些对象:
(pdb) import objgraph
(pdb) objgraph.show_growth()
.
.
.
(pdb) objgraph.show_growth() # this only shows objects that has been added or deleted since last show_growth() call
traceback 4 +2
KeyboardInterrupt 1 +1
frame 24 +1
list 667 +1
tuple 16969 +1
什么在引用这个泄漏的对象?
继续沿着这条路线,我们还可以看到对任何给定对象的引用被保存在哪里。让我们以下面的简单程序为例:
x = [1]
y = [x, [x], {"a":x}]
import pdb; pdb.set_trace()
要查看持有对变量的引用的内容x
,请运行以下objgraph.show_backref()
函数:
(pdb) import objgraph
(pdb) objgraph.show_backref([x], filename="/tmp/backrefs.png")
该命令的输出应该是存储在的 PNG 图像/tmp/backrefs.png
,它应该看起来像这样:
底部带有红色字体的框是我们感兴趣的对象。我们可以看到它被符号引用x
一次,被列表引用了y
三次。如果x
是导致内存泄漏的对象,我们可以使用此方法通过跟踪它的所有引用来查看为什么它没有被自动释放。
转载自:https://juejin.cn/post/7064746559514574878