首页 > 解决方案 > Python中内存泄漏的单元测试

问题描述

有没有办法在 Python 中针对内存泄漏运行单元测试?我已经编写了一些 C++ 绑定代码,并希望确保它不会泄漏内存。

例如,我想要一个像下面这样的测试:

memory_usage_0 = 0
memory_usage_1 = 0
memory_usage_0 = get_memory_usage()
do_cpp_bound_operation()
memory_usage_1 = get_memory_usage()
assert memory_usage_0 == memory_usage_1

标签: pythonmemory-leakspython-c-api

解决方案


如果您愿意让您的单元测试变慢,可以根据您的编码示例尝试以下方法:

将您的代码片段更改为如下所示:

gc.collect()
callCppCoreGenerationFunction()
do_cpp_bound_operation()
gc.collect()
callCppCoreGenerationFunction()
compareCores()

您的 callCppCoreGenerationFunction 可以调用 ac 或 C++ 函数,这就像分叉一个新进程并让子进程杀死自己一样简单,例如,让子进程引用一个空指针。关键是要简单,因为您不希望此函数在内核生成期间通过执行额外的 python 分配来搅浑水。

这里要讨论的关键部分是 compareCores()。为此,您可以使用分叉进程的标准 python 代码,因为您已经有要比较的核心,并且不需要担心会扭曲结果。例如,您可以分叉一个 shell 脚本来计算您的答案。

所以现在的问题是你的 shell 脚本中应该包含什么。它应该做的第一件事是按创建顺序列出内核,以便您可以比较两个最近的内核。然后在 chap 中打开每个内核(开源软件可在https://github.com/vmware/chap获得)。对于每个核心,您应该在 chap 中打开它并在 chap 提示符下运行以下命令:

count leaked

这看起来像这样,将捕获泄漏的本地分配的对象和一些但绝对不是全部泄漏的 python 对象。

chap> count leaked
0 allocations use 0x0 (0) bytes.

该特定命令非常可靠,因为 chap 旨在避免泄漏的误报。因此,您甚至可以只在两个核心中的第二个上运行它,而这样的检查根本不需要第一个核心。如果您确实发现泄漏是可以用来分析泄漏的其他 chap 命令,但如果您只想知道是否有此类泄漏,而不是为什么,那么计数就足够了。

describe used

这将描述所有已分配但未释放的分配(python 和本机)。它的输出看起来像这样,但有更多行:

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

Anchored allocation at 7f505b117670 of size 40
This allocation matches pattern ContainerPythonObject.
This has a PyGC_Head at the start so the real PyObject is at offset 0x10.
This has reference count 1 and python type 0x8fd660 (list)

Anchored allocation at 16f8a80 of size 238
This allocation matches pattern SimplePythonObject.
This has reference count 1 and python type 0x7f5e824cdfe0 (str)
This has a string of length 512 starting with
"bytearray(iterable_of_ints) -> bytearray
bytearray(string, encoding[, errors]".

在该输出中,“%SimplePythonObject”是一些 Python 对象,例如不引用其他 Python 对象的 str,而“%ContainerPythonObject”是可以引用其他 Python 对象的 Python 对象,因此会受到垃圾回收的影响。

请注意,给出了每个此类对象的 python 引用计数。这意味着,在回答您关于检查您的 c++ 代码是否错误地完成引用计数的问题时,这将反映在输出文件中给定对象的引用计数的更改中。因此,可以通过这种方式捕获小到可能导致泄漏的单个引用计数错误。

%SimplePythonObject 通常可以被检测为泄漏,只需对一个核心使用“泄漏计数”,只要没有分配(python 或本机或...)仍然引用它,但使用比较“描述使用”的输出是更通用,并为您的用例提供更好的覆盖范围。


推荐阅读