首页 > 解决方案 > 复杂工作流程的 Python 内存泄漏

问题描述

Python 3 问题在这里。过去 3 天我一直在寻找内存泄漏,但找不到它。我的代码太复杂而无法发布,但基本上是一个 REST API(flask)调用一个预测例程,该例程又调用一个子模块,该子模块又一次异步运行 1000 次计算,将它们返回到一个公共对象。工作流中有许多复杂的数据结构,例如数据框的字典。没有使用(主要)全局变量,但有几个函数确实引用了非本地变量。无论如何,重复运行时内存会爆炸。

我已将其缩小到子模块,即使关闭异步处理,泄漏仍然会发生,这就是令人困惑的地方。如果我只运行子模块,退出该范围,然后查看 Pycharm 调试器中的变量,则不再有任何大数据结构在范围内(这是所需的)。但是内存没有被释放。

我已经尝试过 gc 中的功能,例如 collect() 和 get_objects。我还尝试了一些返回类型引用计数的各种库。尽管我注意到我通常有 43,000 个对“函数”的引用和 19,000 个对“dict”的引用,但这让我非常担心数据帧的 dicts 仍然以某种方式存在. 我已经尝试过明确的事情来擦除字典,比如将它们设置为 None、dict.clear() 和 del。这些运行并且调试器说它们已经消失了。

几个问题 - 我假设 GC 没有释放内存,因为以某种方式存在对其中一些数据结构的引用。但是,一旦我离开该函数的范围并且没有在全局范围内定义任何内容,这样的引用怎么可能存在呢?另外,我真的想要一种简单的方法来查看内存中按大小排序的所有对象。那可能吗?

我知道我只提供了一般信息,但我的具体代码非常复杂且冗长,所以我真的在这里寻找以前遇到过此类问题的人的任何一般性建议,或者我在戒指上写的东西某人的钟声......任何想法都非常感谢。谢谢。

编辑:

好的,这是一个简单的循环,在概念上与我正在做的没有太大区别。它只是一遍又一遍地制作一个数据框字典。当我在这个例子中观察内存时,它的行为与预期一样,内存在每次迭代时出现峰值然后下降。现在在我的实际代码中(这要复杂得多)......我们看到内存增加了,但是在返回时它并没有减少。

那么,从概念上讲,是什么导致了这种情况。对字典或底层数据框的未删除引用,对吧?我查看了 objgraph 的输出,并且没有在函数返回时不应删除的反向引用。(但我想一定有)。

另一个想法:对 objgraph 中数据帧的唯一反向引用是一个元组。现在发生了这种情况,因为在我的实际循环中,数据帧被创建并以作为元组传递的多个返回值结束,例如 inner_sub_func() 返回 (float, float, int, DataFrame)。我猜这个实际的元组在我的主要功能结束时仍然浮动。但是,一旦 main 函数退出并且我们返回到main可以这么说,那个元组不应该被擦除吗?

def func():
    DICT_OF_DF = {}
    for i in range (0, 1000):
        DICT_OF_DF[i] = pd.DataFrame(np.random.random([1000, 1000]))
    DICT_OF_DF.clear()
    del DICT_OF_DF
    gc.collect()
    return 42

for j in range(0, 100000):
    print('Running func #{}'.format(j))
    func()
    debug = True

标签: pythonmemory

解决方案


推荐阅读