运行中的 Python 进程的内存使用情况分析

6
我有一个Python进程,在长时间运行(至少10小时,有时更长)后开始泄漏内存。这个问题很难重现,因此当出现问题时,我希望能够连接到正在运行的Python解释器,并以某种方式检查内存使用情况,例如通过获取当前分配最多内存的对象列表。与通常的性能分析工具(如tracemallocmemory-profiler)相比,这很困难,因为它们需要成为代码的一部分或与进程一起启动,并且对运行时性能产生显着影响。我想要的是一个采样分析器,可以像py-spy一样简单地连接到现有的Python进程,但py-spy只提供函数CPU时间的洞察,而不是内存使用情况。是否有其他工具或不同的方法可以帮助我获得对现有Python进程内存使用量的了解?

编辑:我刚发现了pyrasite,它提供了pyrasite-memory-viewer命令,这正是我要找的,但不幸的是,该项目似乎已经被放弃,我无法在Python 3.8上使用它。


这可能会有所帮助 https://dev59.com/mHRB5IYBdhLWcg3wr44U - sahasrara62
1
@sahasrara62 抱歉,那不是我要找的。所有的答案都是关于需要集成到应用程序中或者至少应用程序需要由分析器启动的分析工具。我正在寻找一种在运行时检查内存使用情况的方法,而不需要预先集成分析器。 - klamann
PyCharm有一些分析器,但不确定它们是否支持运行时。 - sahasrara62
请注意,您安装 pyrasite 的 Python 版本不需要与您要检查的 Python 版本相同,您仍然可以使用跨 Python 版本使其正常工作。 - Maarten Derickx
2个回答

1

https://github.com/vmware/chap(开源代码)可以实现您在此请求的功能,只要您能在Linux上运行应用程序。

  1. 等待直到您认为此时应用程序具有的分配情况很有趣。

  2. 为您的进程收集一个活动核心(例如使用gcore),但首先确保设置了核心转储过滤器,如下所示:

    echo 0x37 >/proc/pid-of-your-python-program/coredump_filter

    gcore pid-of-your-python-program

  3. 在chap中打开生成的核心文件。

  4. 从chap提示符处执行以下操作:

    redirect on

    describe used

  5. 编辑生成的文件或使用所选工具进行后处理。

生成的文件将对当前正在使用的每个分配条目进行记录。 对于与Python对象相对应的条目,它们通常会显示Python类型,如:

Anchored allocation at 7f5e7bf2a570 of size 40
This allocation matches pattern ContainerPythonObject.
This has a PyGC_Head at the start so the real PyObject is at offset 0x18.
This has reference count 1 and python type 0x7f5e824c08a0 (dict)

Anchored allocation at 7f5e7bf2a5b0 of size 40
This allocation matches pattern SimplePythonObject.
This has reference count 3 and python type 0x7f5e824cdfe0 (str)
This has a string of length 12 containing
"ETOOMANYREFS".

在chap中还有其他命令可用于理解你感兴趣的任何分配是如何锚定的,基本上是因为它们允许您通过传入引用向后遍历,但以上内容足以使您能够弄清具有高计数的哪些类型的分配。

例如,假设您想了解在0x7f5e7bf2a570分配中的那个字典是如何被引用的。您可以执行以下命令:

chap> describe incoming 7f5e7bf2a570 /skipUnfavoredReferences true
Anchored allocation at 17fda90 of size 1a8
This allocation matches pattern PyDictKeysObject.

1 allocations use 0x1a8 (424) bytes.

你可以反过来问那个 PyDictKeysObject 的引用是什么(它不是 Python 类型,但用于存储字典的键)。
chap> describe incoming 17fda90 /skipUnfavoredReferences true
Anchored allocation at 7f5e7c0e01f0 of size 40
This allocation matches pattern ContainerPythonObject.
The garbage collector considers this allocation to be reachable.
This has a PyGC_Head at the start so the real PyObject is at offset 0x18.
This has reference count 1 and python type 0x7f5e824c08a0 (dict)

1 allocations use 0x40 (64) bytes.

我不确定我是否理解正确,但是chap会给我一堆内存地址、它们的分配大小、引用等等。但是我如何找出哪些Python对象映射到这些地址?当chap告诉我在0x42处分配了一些数据时,我接下来该怎么做? - klamann
我会修改答案,添加一个“describe used”的输出部分示例。 - Tim Boddy
谢谢提供这个例子,我想我现在明白chap对我有什么用了。然而,我不认为我可以用这种方法找到我的问题的根本原因,因为chap只能识别一些基本的Python类型,但不能确定它们属于哪个类或哪个包。知道某个地方有一个分配了大量内存的dict并不是很有帮助,因为在Python中每个对象基本上都是一个dict。 - klamann
通常可以通过跟踪引用回到类似框架或模块的东西来确定字典的使用方式。 - Tim Boddy

0
我相信memray attach选项可能是你正在寻找的。 但在附加memray到一个进程之前,请确保已安装lldb或gdb。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接