objgraph - Python Object Graphs - 内存泄漏 (Memory Leak)

objgraph是一个Python模块,用于直观地探索Python对象图,帮助发现内存泄漏。通过显示对象引用和反向引用,可以追踪导致对象保留在内存中的原因。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

objgraph - Python Object Graphs - 内存泄漏 (Memory Leak)

https://pypi.org/project/objgraph/
https://github.com/mgedmin/objgraph

objgraph is a module that lets you visually explore Python object graphs.
objgraph 是一个模块,可让您直观地浏览 Python 对象图。

1. Installation

sudo pip3 install objgraph

strong@foreverstrong:~$ sudo pip3 install objgraph
[sudo] password for strong: 
The directory '/home/strong/.cache/pip/http' or its parent directory is not owned by the current user and the cache has been disabled. Please check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
The directory '/home/strong/.cache/pip' or its parent directory is not owned by the current user and caching wheels has been disabled. check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
Collecting objgraph
  Downloading https://files.pythonhosted.org/packages/7d/21/b8ea10bea21a3ecb603ab0a8a59e49282d83eadba16e47464193b0b70dce/objgraph-3.4.1-py2.py3-none-any.whl
Requirement already satisfied (use --upgrade to upgrade): graphviz in /usr/local/lib/python3.5/dist-packages (from objgraph)
Installing collected packages: objgraph
Successfully installed objgraph-3.4.1
strong@foreverstrong:~$

2. Documentation

https://mg.pov.lt/objgraph/

2.1 Quick start

Try this in a Python shell:

strong@foreverstrong:~$ python3
Python 3.5.2 (default, Oct  8 2019, 13:06:37) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> x = []
>>> x
[]
>>> y = [x, [x], dict(x=x)]
>>> y
[[], [[]], {'x': []}]
>>> 
>>> import objgraph
>>> objgraph.show_refs([y], filename='sample-graph.png')
Graph written to /tmp/objgraph-txh1vhmd.dot (4 nodes)
Image generated as sample-graph.png
>>>

If you’ve installed xdot, omit the filename argument to get the interactive viewer.
如果您已经安装了 xdot,请省略 filename 参数以获取交互式查看器。

You should see a graph like this:

在这里插入图片描述
If you prefer to handle your own file output, you can provide a file object to the output parameter of show_refs and show_backrefs instead of a filename. The contents of this file will contain the graph source in DOT format.
如果您喜欢处理自己的文件输出,则可以向 show_refs and show_backrefsoutput 参数提供文件对象,而不是文件名。该文件的内容将包含 DOT 格式的图形源。

2.2 Backreferences

strong@foreverstrong:~$ python3
Python 3.5.2 (default, Oct  8 2019, 13:06:37) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> x = []
>>> x
[]
>>> y = [x, [x], dict(x=x)]
>>> y
[[], [[]], {'x': []}]
>>> 
>>> import objgraph
>>> objgraph.show_refs([y], filename='sample-graph.png')
Graph written to /tmp/objgraph-txh1vhmd.dot (4 nodes)
Image generated as sample-graph.png
>>> 
>>> objgraph.show_backrefs([x], filename='sample-backref-graph.png')
Graph written to /tmp/objgraph-5irdq6jp.dot (18 nodes)
Image generated as sample-backref-graph.png
>>>

and you’ll see

在这里插入图片描述

2.3 Memory leak example (内存泄漏)

The original purpose of objgraph was to help me find memory leaks. The idea was to pick an object in memory that shouldn’t be there and then see what references are keeping it alive.
objgraph 的最初目的是帮助我发现内存泄漏。这个想法是要在内存中选择一个不应该存在的对象,然后查看哪些引用使该对象保持活动状态。

To get a quick overview of the objects in memory, use the imaginatively-named show_most_common_types():
要快速了解内存中的对象,请使用富有想象力的名称 show_most_common_types()

>>> objgraph.show_most_common_types() # doctest: +RANDOM_OUTPUT
tuple                      5224
function                   1329
wrapper_descriptor         967
dict                       790
builtin_function_or_method 658
method_descriptor          340
weakref                    322
list                       168
member_descriptor          167
type                       163
strong@foreverstrong:~$ python3
Python 3.5.2 (default, Oct  8 2019, 13:06:37) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> x = []
>>> x
[]
>>> y = [x, [x], dict(x=x)]
>>> y
[[], [[]], {'x': []}]
>>> 
>>> import objgraph
>>> objgraph.show_refs([y], filename='sample-graph.png')
Graph written to /tmp/objgraph-txh1vhmd.dot (4 nodes)
Image generated as sample-graph.png
>>> 
>>> objgraph.show_backrefs([x], filename='sample-backref-graph.png')
Graph written to /tmp/objgraph-5irdq6jp.dot (18 nodes)
Image generated as sample-backref-graph.png
>>> 
>>> objgraph.show_most_common_types()
function                   2149
dict                       1130
wrapper_descriptor         985
tuple                      924
weakref                    828
builtin_function_or_method 706
method_descriptor          691
set                        386
getset_descriptor          363
list                       331
>>> 
>>> exit()
strong@foreverstrong:~$

But that’s looking for a small needle in a large haystack. Can we limit our haystack to objects that were created recently? Perhaps.
但这就是在大海捞针中寻找一根小针。我们可以将干草堆限制为最近创建的对象吗?也许。

needle [ˈniːdl]:n. 针,指针,刺激,针状物 vi. 缝纫,做针线 vt. 刺激,用针缝
haystack [ˈheɪstæk]:n. 干草堆,比喻如大海捞针般难找

Let’s define a function that “leaks” memory
让我们定义一个泄漏内存的函数

>>> class MyBigFatObject(object):
...     pass
...
>>> def computate_something(_cache={}):
...     _cache[42] = dict(foo=MyBigFatObject(),
...                       bar=MyBigFatObject())
...     # a very explicit and easy-to-find "leak" but oh well
...     x = MyBigFatObject() # this one doesn't leak

We take a snapshot of all the objects counts that are alive before we call our function
在调用函数之前,我们会对所有活动的对象计数进行快照

>>> objgraph.show_growth(limit=3) # doctest: +RANDOM_OUTPUT
tuple                  5228     +5228
function               1330     +1330
wrapper_descriptor      967      +967

and see what changes after we call it

>>> computate_something()
>>> objgraph.show_growth() # doctest: +RANDOM_OUTPUT
MyBigFatObject        2        +2
dict                797        +1

It’s easy to see MyBigFatObject instances that appeared and were not freed. I can pick one of them at random and trace the reference chain back to one of the garbage collector’s roots.
很容易看到 MyBigFatObject 实例出现了但没有释放。我可以随机选择其中之一,并将引用链追溯到垃圾收集器的根之一。

freed:free 的过去式和过去分词
sake [seɪk; ˈsɑːki]:n. 目的,利益,理由,日本米酒

For simplicity’s sake let’s assume all of the roots are modules. objgraph provides a function, is_proper_module(), to check this. If you’ve any examples where that isn’t true, I’d love to hear about them (although see Reference counting bugs).
为了简单起见,让我们假设所有的根都是模块。objgraph 提供了一个函数 is_proper_module() 进行检查。如果您有任何不正确的示例,我很想听听它们 (尽管请参阅 Reference counting bugs)。

>>> import random
>>> objgraph.show_chain(
...     objgraph.find_backref_chain(
...         random.choice(objgraph.by_type('MyBigFatObject')),
...         objgraph.is_proper_module),
...     filename='chain.png')
Graph written to ...dot (13 nodes)
Image generated as chain.png

在这里插入图片描述

It is perhaps surprising to find linecache at the end of that chain (apparently doctest monkey-patches it), but the important things - computate_something() and its cache dictionary - are in there.
在该链的末尾找到线缓存可能很令人惊讶 (apparently doctest monkey-patches it),但是重要的东西 - computate_something() 及其缓存字典就在其中。

There are other tools, perhaps better suited for memory leak hunting: heapy, Dozer.
还有其他工具,也许更适合于内存泄漏搜寻:heapy, Dozer。

2.4 Reference counting bugs

Bugs in C-level reference counting may leave objects in memory that do not have any other objects pointing at them. You can find these by calling get_leaking_objects(), but you’ll have to filter out legitimate GC roots from them, and there are a lot of those:
C 级引用计数中的错误可能会将没有任何其他对象指向它们的对象保留在内存中。您可以通过调用 get_leaking_objects() 来找到它们,但是您必须从它们中过滤出合法的GC根,并且其中有很多:

>>> roots = objgraph.get_leaking_objects()
>>> len(roots) # doctest: +RANDOM_OUTPUT
4621
>>> objgraph.show_most_common_types(objects=roots)
... # doctest: +RANDOM_OUTPUT
tuple          4333
dict           171
list           74
instancemethod 4
listiterator   2
MemoryError    1
Sub            1
RuntimeError   1
Param          1
Add            1
>>> objgraph.show_refs(roots[:3], refcounts=True, filename='roots.png')
... # doctest: +NODES_VARY
Graph written to ...dot (19 nodes)
Image generated as roots.png

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值