3

我知道 python 有自己的内存管理实现,使用区域来处理不同大小的对象等等,尽管我还没有找到完整的文档。我仍然想了解幕后发生的事情。

背景是一个长时间运行的 python2 数据库应用程序,它似乎以某种方式泄漏内存,它运行在 64 位 linux 上。该应用程序每天都会从数据库中读取一些数据,仅用于读取行(使用 MySQLdb)的 RAM 使用量总计约为 3.5GB。大约有 350 万行,之后减少到 100 行,其余行超出范围(“释放”)。

但是 python-2.7 只释放了一小部分现在“未使用”的内存。我知道内存稍后会被重用,但我观察到不知何故,这个内存似乎“慢慢泄漏”。上面提到的 DB 应用程序每天都会读取大量数据。连续读取两次(或多次)只会为第一次读取分配内存,然后显然会重用该内存。但是让它运行几个小时,然后再次读取数据库数据会产生下一个 3+GB 的内存分配峰值(这又是永远不会释放的)。

为了添加更多背景信息(并且让事情变得更糟),我不得不说这个 DB 应用程序不是空闲的,而是永久执行任务。通过监控内存使用情况(nagios 性能数据),我非常确定如果没有这个特定的数据库查询,内存使用量永远不会攀升到 3.5GB RAM(甚至接近)。但是启用此查询会每天增加 3+GB RAM。有问题的查询主要返回唯一的整数和浮点数。

这是我开始怀疑python的主要原因。我觉得我已经阅读了大量信息,查看了 _PyObject_DebugMallocStats() 但不知道 python 决定保留几个千兆字节是什么(或为什么)。

它归结为一个非常简单的示例(不代表有关数据的真实生活情况,我知道 xrange()):

def mem_usage(pid=None):
    mem = 0
    proc = str(pid or "self")
    with open("/proc/%s/smaps" % proc) as fstat:
        for l in fstat:
            if not l.startswith("Private_"):
                continue
            mem += int(l.split(":", 1)[1].strip().split(" ", 1)[0])
    return mem

mem_usage()                 # reports a few MB
x = list(range(100000000))  # use list() for py3k
mem_usage()                 # reports ~3GB
del x
mem_usage()                 # reports ~2.5GB

有趣的是,当我删除巨大的列表时,py3k 会释放内存。不仅是一小部分,而且几乎所有留下的内存使用量仅比开始时略高。

我已经用 memory_profiler 对此进行了调查(我猜它的作用并不比给定的 mem_usage() 函数多多少)而没有任何洞察力。我已经阅读了有关 gdb-heap 的信息,但到目前为止还无法正常工作。

我实际上不相信有解决方案(除了重新启动应用程序或减少从数据库读取的数据量)。但我真的很感激关于这个话题的任何见解。

编辑:

总结一下我的问题:为什么 python-2.7 保持分配这个内存?

4

1 回答 1

2

range示例保留了大量内存,因为 Python 2.7 从不释放ints:

block_list 是所有已分配的 PyIntBlocks 的单链表,通过它们的下一个成员链接。PyIntBlocks 在关闭之前永远不会返回到系统(PyInt_Fini)。

然而,这应该不是问题,除非在某个时候,几个 GB 的 int 同时处于活动状态。否则,Python 将使用旧的、丢弃的整数来表示您使用的任何新整数。如果您确实有几 GB 的实时整数,我建议您想办法一次保留更少的整数。

于 2013-12-17T11:36:51.800 回答