2016-09-22 37 views
0

编辑:没关系,我只是完全愚蠢。隐藏引用导致大量内存使用的函数参数?

我碰到与小串递归代码来了,这里是它的本质加上我的测试的东西:

def f(s): 
    if len(s) == 2**20: 
     input('check your memory usage again') 
    else: 
     f(s[1:]) 
input('check your memory usage, then press enter') 
f('a' * (2**20 + 500)) 

通话之前,我的Python过程大约需要9 MB(通过Windows任务管理器作为托运) 。在〜1MB字符串的500个级别之后,它的大小约为513 MB。毫不奇怪,因为每个调用级别在其变量s中仍然保留其字符串。

但我试图通过更换的参考字符串到新的字符串的引用来解决它,它仍然上升到513 MB:

def f(s): 
    if len(s) == 2**20: 
     input('check your memory usage again') 
    else: 
     s = s[1:] 
     f(s) 
input('check your memory usage, then press enter') 
f('a' * (2**20 + 500)) 

为什么不,让熄灭记忆?字符串甚至只是变小,所以后来的字符串很容易适应早期字符串的空间。有没有隐藏额外的字符串引用或发生了什么?

我已经预料到这样的表现,只上升到10 MB(1 MB的变化,符合市场预期,而老的字符串仍然存在,因为新的字符串建):

input('check your memory usage, then press enter') 
s = 'a' * (2**20 + 500) 
while len(s) != 2**20: 
    s = s[1:] 
input('check your memory usage again') 

(不要介意穷人的时间复杂性,顺便说一句,我知道,不要打扰。)

+0

我看到两者之间没有什么区别,说实话。 –

+1

@StefanPochmann,如果你改用'del s [0]'会发生什么? – BallpointBen

+0

@James然后我得到'TypeError:'str'对象不支持项目删除'。你没有明白吗?你使用的是什么Python版本? –

回答

2

你的函数是递归的,所以当你调用f()时,你的当前帧被放到一个堆栈,并创建一个新的。所以基本上每个函数调用都会引用它创建的新字符串以传递给下一个调用。

为了说明叠层

import traceback 

def recursive(x): 
    if x: 
     recursive(x[1:]) 
    else: 
     traceback.print_stack() 

recursive('abc') 

给出

$ python tmp.py 
    File "tmp.py", line 10, in <module> 
    recursive('abc') 
    File "tmp.py", line 5, in recursive 
    recursive(x[1:]) 
    File "tmp.py", line 5, in recursive 
    recursive(x[1:]) 
    File "tmp.py", line 5, in recursive 
    recursive(x[1:]) 
    File "tmp.py", line 7, in recursive 
    traceback.print_stack() 

何时recursive()返回最终呼叫,则返回回到它上面的下一个呼叫,其仍具有参照x

But I tried to fix it by replacing the reference to the string with a reference to the new string and it still goes up to 513 MB

那么你在当前的功能的确被调用,但调用它仍具有参考什么传入的功能。例如,

def foo(x): 
    print "foo1", locals() 
    bar(x) 
    print "foo2", locals() 

def bar(x): 
    print "bar1", locals() 
    x = "something else" 
    print "bar2", locals() 

foo('original thing') 

foo()被调用时,它通过串'original thing'bar()。而且即使bar()则摆脱了参考的,上面foo()当前呼叫仍然具有参考

$ python tmp_x.py 
foo1 {'x': 'original thing'} 
bar1 {'x': 'original thing'} 
bar2 {'x': 'something else'} 
foo2 {'x': 'original thing'} 

我希望说明它。我在关于堆栈帧的第一个陈述中有点模糊。

+1

谢谢。我只是在那里脑筋急转弯。如果它更快地到达这一点,我会接受你的答案,现在我真的更喜欢我的答案。你介意在你的顶部添加它还是类似的东西? –

+0

@StefanPochmann不够公平,我喜欢华夫饼!我添加了一些更直接的信息。 –

+0

嗯,我猜这也很难说出我在想什么,以及对我说什么:-)。我已经接受了你的答案,因为虽然我自己的答案直接解决了我错误的想法,但这是我错误的想法,所以我的答案可能不适用于其他人,你的答案可能总体上更好...... –

1

Are there hidden additional references to the strings somewhere or what is going on

好,每个功能都有它的字符串的引用,而它在栈上,所以s = s[1:]仍然要保持s[1:]在未来的函数调用。经过500次递归调用后,每次只复制1个字符的事实对于每次传递的大约2 ** 20个字符来说都是微不足道的。

0

虽然每个调用级别确实摆脱自己的旧字符串,它创建保持自己的字符串。

(只是把这个在我自己的话阅读其他的答案后,更直接地解决了我(作者问题)已经错过了。)