【问题标题】:Profile Memory Usage of a running Python Process分析正在运行的 Python 进程的内存使用情况
【发布时间】:2022-01-16 20:35:29
【问题描述】:

我有一个 Python 进程在很长一段时间(至少 10 小时,有时更多)后开始泄漏内存。这个问题很难重现,因此我想在问题出现时附加到正在运行的 Python Interpreter 并以某种方式检查内存使用情况,例如通过获取当前分配最多内存的对象列表。

这对于tracemallocmemory-profiler 等常用的分析工具来说是困难的,因为它们需要成为代码的一部分或与进程一起启动,并且它们对运行时性能有重大影响。

我想要的是一个采样分析器,我可以简单地将它附加到现有的 Python 进程,如 py-spy,但 py-spy 只能让我了解函数花费的 CPU 时间,而不是内存使用情况。

是否有其他工具或不同的方法可以帮助我深入了解现有 Python 进程的内存使用情况?

edit:我刚刚找到了pyrasite,它提供了pyrasite-memory-viewer 命令,这正是我正在寻找的,但不幸的是,该项目似乎被放弃,我无法让它在 Python 3.8 上运行。

【问题讨论】:

  • 这可能会有所帮助*.com/questions/552744/…
  • @sahasrara62 抱歉,这不是我要找的。所有的答案都是关于需要集成到应用程序中的分析工具,或者至少应用程序需要由分析器启动。我正在寻找一种在运行时检查内存使用情况的方法,而无需之前集成任何探查器。
  • pycharm 有一些分析器,但不知道它们是否在运行时支持
  • 请注意,您安装 pyrasite 的 python 不需要与您检查的 python 相同,您仍然可以使用跨 python 版本让它工作

标签: python python-3.x memory-leaks profiling


【解决方案1】:

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

  1. 等到您认为您对应用程序在该时间点的分配感兴趣。

  2. 为您的进程收集一个实时核心(例如使用 gcore),但请确保首先设置 coredump 过滤器,如下所示:

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

    gcore pid-of-your-python-program

  3. 在章节中打开生成的核心。

  4. 在章节提示符下执行以下操作:

    重定向

    描述使用过的

  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 处的 dict 是如何被引用的。您可以执行以下命令:

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.

【讨论】:

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