如何释放由“for”引起的内存?

3
我们的游戏程序会将所有玩家的数据初始化到内存中。我的目标是减少不必要的内存占用。我追踪了程序并发现“for”很占用内存。
例如:
 Line #    Mem usage    Increment   Line Contents
================================================
    52                             @profile
    53    11.691 MB     0.000 MB   def test():
    54    19.336 MB     7.645 MB       a = ["1"] * (10 ** 6)
    55    19.359 MB     0.023 MB       print recipe.total_size(a, verbose=False)
    56    82.016 MB    62.656 MB       for i in a:
    57                                     pass

print recipe.total_size(a, verbose=False):8000098字节

问题是我如何释放那62.656 MB的内存。

P.S.

对不起,我知道我的英语不是很好。我将感谢每个人阅读这个。:-)


5
我担心你的分析在这里不正确。for语句只是为列表a创建了一个迭代器对象,它实际上仅仅是一个对a的引用以及一个用于跟踪迭代器在列表中位置的索引值。没有更多其他操作 - Martijn Pieters
我使用的工具是memory_profiler。虽然我不太理解它,但在github上很受欢迎。(https://github.com/fabianp/memory_profiler) 如果“for”已经完成了它应该做的事情,那么数字(62.656 MB)从哪里来? - wangbuze
我的猜测是这里的问题在于列表a;在我的64位Mac上,一个包含100万个字符串的列表需要近44 MB的内存;即使对于32位系统,报告的7MB也远远不够。我使用sys.getsizeof()函数计算了这个大小,以测量单个“1”字符串乘以100万的内存需求,再加上具有100万个引用的列表对象的大小。 - Martijn Pieters
嗯,除非'1'字符串被内部化(在我的测试中是这样的); 那么列表确实只占用7.6 MB,因此这是一致的。这里的另一个候选者是recipe.total_size()函数。 - Martijn Pieters
感谢您的耐心。在我的实际程序中,我发现了一件我无法理解的事情。容器占用的内存比“for”循环要小得多。因此,我进行了这个演示测试。“for”循环引起的内存不等于容器引起的内存。一个是7.645 MB,另一个是62.656 MB。您使用sys.getsizeof()计算列表大小的方式,我也这样做了。我的目的是减少内存。因此,62.656 MB对我比7.645 MB更重要。 :-) - wangbuze
2个回答

0
如果你非常迫切地想要在循环中减少内存使用量,你可以这样做:
i = 0
while 1:
    try:
        a[i]  #accessing an element here
        i += 1
    except IndexError:
        break

内存统计信息(如果它们是准确的):

12     9.215 MB     0.000 MB       i = 0
13     9.215 MB     0.000 MB       while 1:
14    60.484 MB    51.270 MB           try:
15    60.484 MB     0.000 MB               a[i]
16    60.484 MB     0.000 MB               i += 1
17    60.484 MB     0.000 MB           except IndexError:
18    60.484 MB     0.000 MB               break

然而,这段代码看起来很丑陋和危险,并且减少内存使用量只是微不足道的。

为什么你会使用range而不是for i in range(...) - David Heffernan
xrange会产生值而不是将它们存储在内存中。因此,在处理大序列时,可以帮助节省内存。然而,遗憾的是,在这种特殊情况下,似乎并没有起到作用,可能是因为它只产生整数。这就是为什么我改变了我的解决方案,从内存使用的角度来看更好,但仅限于此。 - Lukasz
@jentyk:你做得很好,确实减少了一些内存使用。但我不能在我的项目中使用它,你知道为什么。:-) 我期待一个更好的想法。 - wangbuze
注意:我不能使用xrange。因为列表“a”是我的项目中的查询结果。 - wangbuze
问题定义得不是很清楚,我有点担心。我不确定您是希望减少整体内存使用还是在代码的特定位置?如果a是一个查询,也许使用ORM和延迟加载会对您有所帮助?为了回答这个问题而不是继续猜测,我们需要一个明确定义的问题。 - Lukasz
你说得对,这是我的错。下次我会尽力做得更好。谢谢你的建议。 - wangbuze

0

1) 在编写代码时,应该使用 generator 而不是 list iterator。根据您提供的示例代码:

@profile
def test():
    a = ("1" for i in range(10**6))   #this will return a generator, instead of a list.
    for i in a:
        pass

现在如果你在for循环中使用生成器'a',它不会占用太多的内存。

2)如果你得到一个列表,那么首先将其转换为生成器

@profile
def test():
    a = ["1"] * (10**6)  #getting list
    g = (i for i in a)   #converting list into a generator object
    for i in g:          #use generator object for iteration
        pass

试一试。如果对你有帮助。


谢谢,但在我的真实程序中,查询结果是列表"a"的替代品。Twisted dbapi返回它。它无法转换为生成器。这让我很生气。 - wangbuze
它在这个演示测试中是有效的。但在我的游戏程序中,“a”是来自数据库的查询结果。它是一个列表。 - wangbuze
它占用了63.867 MB的内存。 - wangbuze

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接