likes
comments
collection
share

Python 性能分析之内存使用

作者站长头像
站长
· 阅读数 11

上一篇文章我们已经掌握了如何查看代码中的时间消耗,今天我们继续讲讲程序中的内存消耗,当然对于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_profilermemory_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,它应该看起来像这样:

Python 性能分析之内存使用

底部带有红色字体的框是我们感兴趣的对象。我们可以看到它被符号引用x一次,被列表引用了y三次。如果x是导致内存泄漏的对象,我们可以使用此方法通过跟踪它的所有引用来查看为什么它没有被自动释放。