为什么尾部调用优化需要垃圾收集?是因为如果你在一个你想要进行尾调用的函数中分配内存,那么就没有办法进行尾调用并重新获得那个内存? (因此必须保存堆栈,以便在尾调用后,可以回收内存。)
答案 0 :(得分:9)
像大多数神话一样,这个可能有一点道理。虽然GC不是必需用于尾部调用优化,但在少数情况下它肯定帮助。假设你在C ++中有这样的东西:
int foo(int arg) {
// Base case.
vector<double> bar(10);
// Populate bar, do other stuff.
return foo(someNumber);
}
在这种情况下,返回foo(someNumber); 看起来像<尾部调用,但由于你正在使用RAII内存管理,并且你必须自由禁止,这一行将转换为更低级别,如下所示(非正式伪代码):
ret = foo(someNumber);
free(bar);
return ret;
如果您有GC,则编译器无需将指令插入空闲栏。因此,此函数可以优化为尾调用。
答案 1 :(得分:7)
你在哪里听到的?
即使没有任何垃圾收集器的C编译器也能够优化对迭代等效的尾递归调用。
答案 2 :(得分:4)
尾部调用优化不需要垃圾收集。
在调用堆栈上分配的任何变量都将在递归调用中重用,因此那里没有内存泄漏。
无论是否使用尾调用优化,在堆调用之前分配的任何局部变量都不会在尾调用之前释放。无论是否使用尾调用优化,在堆调用之前分配并在尾调用之前释放的局部变量都不会泄漏内存。
答案 3 :(得分:2)
确实,尾部调用优化并不真正需要垃圾收集。
但是,假设你有1 GB的RAM,你想要过滤900MB的整数列表,只保留正数。假设大约一半是正面的,一半是负面的。
在使用GC的语言中,您只需编写该函数即可。 GC会发生很多次,你最终会得到一个450 MB的列表。代码如下所示:
list *result = filter(make900MBlist(), funcptr);
make900MBlist将逐步GCd,因为部件过滤器已经过去不再被任何东西引用。
在没有GC的语言中,为了保留尾递归,你必须做这样的事情:
list *srclist = make900MBlist();
list *result = filter(srclist, funcptr);
freelist(srclist);
在最终释放srclist之前,最终必须使用900MB + 450MB,因此程序将耗尽内存并失败。
如果您编写自己的filter_reclaim,则不再需要释放输入列表:
list *result = filter_reclaim(make900MBlist(), funcptr);
它不再是尾递归的,你可能会溢出你的堆栈。