为什么尾部调用优化需要垃圾收集?
为什么尾调用优化需要垃圾收集? 是否因为如果您在一个函数中分配内存,然后想要对其进行尾部调用,则无法进行尾部调用并重新获得该内存? (因此必须保存堆栈,以便在尾部调用之后可以回收内存。)
Why is garbage collection required for tail call optimization? Is it because if you allocate memory in a function which you then want to do a tail call on, there'd be no way to do the tail call and regain that memory? (So the stack would have to be saved so that, after the tail call, the memory could be reclaimed.)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
像大多数神话一样,这个神话可能有一定道理。 虽然尾部调用优化不需要 GC,但在某些情况下它确实有帮助。 假设您在 C++ 中有类似的内容:
在本例中,return foo(someNumber); 看起来像尾部调用,但因为您使用的是 RAII 内存管理,并且必须释放内存条,所以该行将转换为较低级别,如下所示(非正式伪代码):
如果您有 GC ,编译器没有必要向空闲栏插入指令。 因此,这个函数可以优化为尾调用。
Like most myths, there may be a grain of truth to this one. While GC isn't required for tail call optimization, it certainly helps in a few cases. Let's say you have something like this in C++:
In this case, return foo(someNumber); looks like a tail call, but because you're using RAII memory management, and you have to free bar, this line would translate to a lower level as follows (in informal pseudocode):
If you have GC, it is not necessary for the compiler to insert instructions to free bar. Therefore, this function can be optimized to a tail call.
你是从哪里听来的?
即使没有任何类型的垃圾收集器的 C 编译器也能够优化对其迭代等效项的尾递归调用。
Where did you hear that?
Even C compilers without any kind of garbage collector are able to optimize tail recursive calls to their iterative equivalent.
尾部调用优化不需要垃圾收集。
调用堆栈上分配的任何变量都将在递归调用中重用,因此不会出现内存泄漏。
无论是否使用尾调用优化,在堆上分配且在尾调用之前未释放的任何局部变量都会泄漏内存。 无论是否使用尾调用优化,在堆上分配并在尾调用之前释放的局部变量都不会泄漏内存。
Garbage collection is not required for tail-call optimization.
Any variables allocated on the call stack will be reused in the recursive call, so there's no memory leak there.
Any local variables allocated on the heap and not freed before a tail call will leak memory whether or not tail-call optimization is used. Local variables allocated on the heap and freed before the tail call will not leak memory regardless of whether or not tail-call optimization is used.
确实,尾部调用优化并不真正需要垃圾收集。
然而,假设您有 1 GB 的 RAM,并且您想要过滤 900MB 的整数列表以仅保留正数。 假设大约一半是积极的,一半是消极的。
在具有 GC 的语言中,您只需编写函数即可。 GC 会发生很多次,最终会得到一个 450 MB 的列表。 代码将如下所示:
make900MBlist 将逐渐被 GCd,因为已完成的部分过滤器不再被任何内容引用。
在没有 GC 的语言中,为了保留尾递归,您必须执行以下操作:
在最终释放 srclist 之前,最终必须使用 900MB + 450MB,因此程序将耗尽内存并失败。
如果您编写自己的filter_reclaim,则会释放输入列表,因为它不再需要:
它将不再是尾递归,并且您可能会溢出堆栈。
It's true, garbage collection is not really required for tail-call optimization.
However, let's say you have 1 GB of RAM and you want to filter a 900MB list of integers to keep only the positive ones. Assume about half are positive, half are negative.
In a language with GC, you just write the function. GC will occur a bunch of times, and you'll end up with a 450 MB list. The code will look like this:
make900MBlist will be incrementally GCd as the parts filter has gone through are no longer referenced by anything.
In a language without GC, to preserve tail-recursion, you'd have to do something like this:
This will end up having to use 900MB + 450MB before finally freeing the srclist, so the program will run out of memory and fail.
If you write your own filter_reclaim, that frees the input list as its no longer necessary:
It will no longer be tail-recursive, and you'll likely overflow your stack.