【问题标题】:Is python2 reluctant to free memory?python2不愿意释放内存吗?
【发布时间】:2013-12-17 10:45:45
【问题描述】:

我知道 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 保持分配这个内存?

【问题讨论】:

  • 所以我在这里阅读了你的文字墙,我很难找到问题......你到底在问什么?你说python不会释放内存,但python3k会......你在找什么?你能用一个清晰​​简洁的可回答问题进行总结吗?
  • 我以我能想到的最短的方式添加了这个问题:)
  • @resi 我们是pythonista。你能把问题写成单行字吗?我能理解的是,为什么 python 2 不释放内存。
  • 这可能是内存碎片问题吗?您是否尝试在其中添加 gc.collect() ?
  • @Chronial:我已经 gc.collect() 处理了所有世代(0、1 和 2),但它不会改变内存消耗。但我也怀疑关于长期运行的数据库应用程序的碎片化(并且 gc.collect() 在那里也没有影响)。

标签: python linux python-2.7 memory python-internals


【解决方案1】:

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

block_list is a singly-linked list of all PyIntBlocks ever allocated, linked via their next members. PyIntBlocks are never returned to the system before shutdown (PyInt_Fini).

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

【讨论】:

  • 我看到 PyIntBlocks 的问题是由同时存在的大量 int 对象引起的(浮点数也是如此)。我不确定我是否正确地阅读了 intobject.c,但是伪释放的块被收集在 free_list 中并在以后重用,这是真的吗?这是否意味着我的 DB 应用程序在第二天再次运行时会用完所有 PyIntBlocks(当它执行内存使用量的另一个飞跃时)?
  • 我不相信在 python 2.7 中处理整数和浮点数是我在这里遇到的唯一问题,但无法获得更多见解。尽管如此,您还是指出了这一点,所以我想接受您的回答是公平的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-10-25
  • 2017-06-29
  • 2011-06-30
  • 2016-09-14
  • 2015-08-19
  • 2012-05-15
  • 2018-01-26
相关资源
最近更新 更多