How to implement closures without gc?我正在设计一种语言。 首先,我想决定要生成什么代码。 该语言将具有类似于javascript的词法闭包和基于原型的继承。 但我不是gc的粉丝,尽量避免。 所以问题是:有没有一种优雅的方法来实现闭包而不需要在堆上分配堆栈帧并将其留给垃圾收集器? 我的第一个想法: 我不会使用高级语言或遵循任何调用约定,所以我可以尽可能多地粉碎堆栈。 (编辑:我知道引用计数是垃圾收集的一种形式,但我使用gc的更常见的含义) 如果您可以通过不使用GC来解释您要避免的内容,那么这将是一个更好的问题。我确信你知道,大多数提供词法闭包的语言都会在堆上分配它们,并允许它们在创建它们的激活记录中保留对变量绑定的引用。
我所知道的唯一替代方法是
简短版本是,您有三个主要选择:
我明白我已经很晚了,但我偶然偶然发现了这个问题。 我认为完全支持闭包确实需要GC,但在某些特殊情况下,堆栈分配是安全的。确定这些特殊情况需要进行一些逃逸分析。我建议你看一下BitC语言文章,比如BitC中的Closure Implementation。 (虽然我怀疑这些文件是否反映了当前的计划。)BitC的设计者也遇到了同样的问题。他们决定为编译器实现一种特殊的非收集模式,它拒绝所有可能逃脱的闭包。如果启用,则会显着限制语言。但是,该功能尚未实现。 我建议你使用收藏家 - 这是最优雅的方式。您还应该考虑一个构建良好的垃圾收集器比malloc更快地分配内存。 BitC人员确实非常重视性能,他们仍然认为即使对于他们的操作系统Coyotos的大多数部分来说,GC都很好。您可以通过简单的方式迁移缺点:
许多人担心垃圾收集器因为他们使用Java的经验。 Java有一个很棒的收集器,但是用Java编写的应用程序由于产生了大量的垃圾而存在性能问题。此外,由于启动和响应时间较长,对于桌面应用程序来说,膨胀的运行时和花哨的JIT编译并不是一个好主意。 这个主题可能会有所帮助,尽管这里的一些答案已经反映了答案。 一张海报很有用:
所以答案是:不,没有优雅的方法来关闭和没有真正的GC。 所以,我的问题反映了其他一些 - 为什么你不想实现GC?简单的标记+扫描或停止+复制需要大约2-300行(Scheme)代码,并且在编程工作方面并不是那么糟糕。在使程序变慢方面:
设计你的语言是可能的,所以没有周期:如果你只能创造新的对象而不是改变旧的对象,并且如果使对象不能进行循环,则循环永远不会出现。 Erlang基本上以这种方式工作,但在实践中它确实使用GC。 C ++ 0x规范定义了没有垃圾收集的lambdas。简而言之,在lambda闭包包含不再有效的引用的情况下,规范允许非确定性行为。例如(伪语法):
此示例中的lambda引用在堆栈上分配的变量( 如果你正在避免垃圾收集,那么我假设你也允许显式堆与堆栈分配和(可能)指针。如果是这种情况,那么你可以像C ++那样做,只是假设使用你的语言的开发人员足够聪明,可以用lambdas发现问题情况并明确地复制到堆中(就像你在返回一个合成的值时那样)一个功能)。 如果你有精确复制GC的机器,你可以在堆栈上初始分配并复制到堆并更新指针,如果你在退出时发现指向这个堆栈帧的指针已经转义。这样,只有在实际捕获包含此堆栈帧的闭包时才需要付费。这有助于帮助还是伤害取决于您使用闭包的频率以及捕获的数量。 您也可以查看C ++ 0x的方法(N1968),尽管人们可能期望从C ++开始,它包括指望程序员指定要复制的内容和引用的内容,如果你弄错了,你就会得到无效的访问。
GC是一般案例的唯一解决方案。 您可以假设所有闭包最终都会被调用一次。现在,当调用闭包时,您可以在闭包返回时进行清理。 你打算如何处理返回的物体?它们必须在某些时候进行清理,这与闭包完全相同。 或者根本不做GC。在某些情况下,最好只是忘记内存泄漏,并在完成后让进程清理完毕。 根据您对GC的疑虑,您可能会担心定期GC扫描。在这种情况下,当项目超出范围或指针发生变化时,您可以执行选择性GC。我不确定这会有多贵。 @Allen 如果在包含函数退出时无法使用它们,那么闭包有什么用呢?从我的理解,这是关闭的全部意义。 迟到总比不到好? 您可能会发现这很有趣:差异执行。 它是一个鲜为人知的控制结构,它的主要用途是编程用户界面,包括可以在使用时动态变化的用户界面。它是模型 - 视图 - 控制器范例的重要替代品。 我提到它是因为人们可能会认为这样的代码会严重依赖于闭包和垃圾收集,但控制结构的副作用是它消除了这两者,至少在UI代码中是这样。 我想如果这个过程非常短,这意味着它不能使用太多内存,那么GC是不必要的。这种情况类似于担心堆栈溢出。不要太深,也不能溢出;不要运行太久,你不能需要GC。清理成为简单回收您预先分配的大区域的问题。即使是较长的进程也可以分成较小的进程,这些进程预先分配了自己的堆。例如,这适用于事件处理程序。如果你正在编写编译器,它不能很好地工作;在这种情况下,GC肯定不是一个障碍。 我已经读过ML的最后版本只能使用GC 创建多个堆栈? |