Programming languages 为什么尾部调用优化需要垃圾收集?
为什么尾部调用优化需要垃圾收集?是不是因为如果你在一个函数中分配内存,然后你想对它进行尾部调用,那么就没有办法进行尾部调用并重新获得内存?因此,必须保存堆栈,以便在尾部调用之后,可以回收内存。您从哪里听说的Programming languages 为什么尾部调用优化需要垃圾收集?,programming-languages,garbage-collection,tail-call-optimization,Programming Languages,Garbage Collection,Tail Call Optimization,为什么尾部调用优化需要垃圾收集?是不是因为如果你在一个函数中分配内存,然后你想对它进行尾部调用,那么就没有办法进行尾部调用并重新获得内存?因此,必须保存堆栈,以便在尾部调用之后,可以回收内存。您从哪里听说的 即使没有任何垃圾收集器的C编译器也能够优化尾部递归调用,使其达到迭代等价 你在哪里听到的 即使没有任何垃圾收集器的C编译器也能够优化尾部递归调用,使其达到迭代等价 尾部调用优化不需要垃圾收集 调用堆栈上分配的任何变量都将在递归调用中重用,因此没有内存泄漏 无论是否使用尾部调用优化,在堆上分配
即使没有任何垃圾收集器的C编译器也能够优化尾部递归调用,使其达到迭代等价 你在哪里听到的
即使没有任何垃圾收集器的C编译器也能够优化尾部递归调用,使其达到迭代等价 尾部调用优化不需要垃圾收集 调用堆栈上分配的任何变量都将在递归调用中重用,因此没有内存泄漏
无论是否使用尾部调用优化,在堆上分配且在尾部调用之前未释放的任何局部变量都将泄漏内存。无论是否使用尾部调用优化,在堆上分配并在尾部调用之前释放的本地变量都不会泄漏内存。尾部调用优化不需要垃圾收集 调用堆栈上分配的任何变量都将在递归调用中重用,因此没有内存泄漏
无论是否使用尾部调用优化,在堆上分配且在尾部调用之前未释放的任何局部变量都将泄漏内存。无论是否使用尾部调用优化,在堆上分配并在尾部调用之前释放的局部变量都不会泄漏内存。确实,尾部调用优化并不需要垃圾收集 但是,假设您有1GB的RAM,并且希望过滤900MB的整数列表,以仅保留正整数。假设一半是正的,一半是负的 在使用GC的语言中,只需编写函数。GC会发生很多次,最终会得到一个450MB的列表。代码如下所示:
list *result = filter(make900MBlist(), funcptr);
list *srclist = make900MBlist();
list *result = filter(srclist, funcptr);
freelist(srclist);
make900MBlist将递增GCd,因为已通过的部件过滤器不再被任何对象引用
在没有GC的语言中,要保留尾部递归,必须执行以下操作:
list *result = filter(make900MBlist(), funcptr);
list *srclist = make900MBlist();
list *result = filter(srclist, funcptr);
freelist(srclist);
在最终释放srclist之前,这将不得不使用900MB+450MB,因此程序将耗尽内存并失败
如果您编写自己的筛选器_reclain,则不再需要输入列表时会释放输入列表:
list *result = filter_reclaim(make900MBlist(), funcptr);
它将不再是尾部递归的,并且很可能会使堆栈溢出。没错,尾部调用优化实际上不需要垃圾收集 但是,假设您有1GB的RAM,并且希望过滤900MB的整数列表,以仅保留正整数。假设一半是正的,一半是负的 在使用GC的语言中,只需编写函数。GC会发生很多次,最终会得到一个450MB的列表。代码如下所示:
list *result = filter(make900MBlist(), funcptr);
list *srclist = make900MBlist();
list *result = filter(srclist, funcptr);
freelist(srclist);
make900MBlist将递增GCd,因为已通过的部件过滤器不再被任何对象引用
在没有GC的语言中,要保留尾部递归,必须执行以下操作:
list *result = filter(make900MBlist(), funcptr);
list *srclist = make900MBlist();
list *result = filter(srclist, funcptr);
freelist(srclist);
在最终释放srclist之前,这将不得不使用900MB+450MB,因此程序将耗尽内存并失败
如果您编写自己的筛选器_reclain,则不再需要输入列表时会释放输入列表:
list *result = filter_reclaim(make900MBlist(), funcptr);
它将不再是尾部递归,并且很可能会溢出堆栈。像大多数神话一样,这一点可能有一点道理。虽然尾部调用优化不需要GC,但在某些情况下它肯定会有所帮助。假设在C++中有类似的东西:
int foo(int arg) {
// Base case.
vector<double> bar(10);
// Populate bar, do other stuff.
return foo(someNumber);
}
如果您有GC,编译器就不必向自由条插入指令。因此,此函数可以优化为尾部调用。与大多数神话一样,此函数可能有一点道理。虽然尾部调用优化不需要GC,但在某些情况下它肯定会有所帮助。假设在C++中有类似的东西:
int foo(int arg) {
// Base case.
vector<double> bar(10);
// Populate bar, do other stuff.
return foo(someNumber);
}
如果您有GC,编译器就不必向自由条插入指令。因此,可以将此函数优化为尾部调用。明显的问题:谁声称尾部调用优化需要垃圾收集?明显的问题:谁声称尾部调用优化需要垃圾收集?我希望看到一个参考,或一些支持这一点的东西。例如,我知道GCC支持这个优化——但是我不知道它如何处理需要清理的C++对象。我很想看到一个引用,或者有什么东西可以支持这个。我知道GCC支持这个优化——但是我不知道它如何处理需要清理的C++对象。+ 1:对尾部调用很有意思,优化RAII,转换成一个UNIQUY的PTR。
如果不是作为参数传递,则在尾部调用之前释放,否则在被调用方中释放。@JonHarrop:通常的解决方法是简单地将主体包装在{}中,并在外部返回。+1:尾部调用优化RAII会很有趣,转换为类似于唯一的\u ptr的东西,如果它没有作为参数传递或在被调用方中解除分配,那么它将在尾部调用之前解除分配。@Jonharop:通常的解决方法是简单地将主体包装在{}中,并在外部返回。C没有RAII,也就是说,尾部调用实际上是尾部调用。在C++中,一些变量在表达式递归调用完成后就结束了范围,这将阻止这种优化,因为需要清理代码,而不是真正的尾部调用。C没有RAII,即尾部调用真的是尾部调用。在C++中,一些变量在表达式递归调用完成后就结束了范围,这将阻止这种优化,因为需要清理运行的代码,而不是真正的尾部调用。