编辑:没关系,我只是完全愚蠢。
我遇到了在越来越小的子串上递归的代码,这里的重点是我的测试内容:
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个级别之后,它在大约513MB处。毫不奇怪,因为每个调用级别仍然保持其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 :(得分: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
。
但是我尝试通过替换对字符串的引用来修复它,并引用了新字符串,它仍然达到了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 :(得分:1)
是否隐藏了对字符串的附加引用或正在进行的内容
好吧,每个函数都有一个对其字符串的引用,而它在栈中,所以s = s[1:]
仍然会在下一个函数调用中保持s[1:]
。在500次递归调用之后,每次复制1个字符的事实对于每次传递的大约2 ** 20个字符来说是无关紧要的。
答案 2 :(得分:0)
虽然每个通话级别 都会删除自己的旧字符串,但创建并且保持自己的新字符串。
(在阅读其他答案之后,用我自己的话来说,更直接地解决我(问题作者)遗漏的问题。)