关于垃圾收集:如何在没有gc的情况下实现闭包?

关于垃圾收集:如何在没有gc的情况下实现闭包?

How to implement closures without gc?

我正在设计一种语言。 首先,我想决定要生成什么代码。 该语言将具有类似于javascript的词法闭包和基于原型的继承。 但我不是gc的粉丝,尽量避免。 所以问题是:有没有一种优雅的方法来实现闭包而不需要在堆上分配堆栈帧并将其留给垃圾收集器?

我的第一个想法:

  • 使用引用计数和垃圾收集周期(我真的不喜欢这个)
  • 使用意大利面条堆栈(看起来非常低效)
  • 限制闭包形成某些上下文,这样我就可以使用返回地址堆栈和本地堆栈。
  • 我不会使用高级语言或遵循任何调用约定,所以我可以尽可能多地粉碎堆栈。

    (编辑:我知道引用计数是垃圾收集的一种形式,但我使用gc的更常见的含义)


    如果您可以通过不使用GC来解释您要避免的内容,那么这将是一个更好的问题。我确信你知道,大多数提供词法闭包的语言都会在堆上分配它们,并允许它们在创建它们的激活记录中保留对变量绑定的引用。

    我所知道的唯一替代方法是gcc用于嵌套函数:为函数创建一个trampoline并将其分配到堆栈中。但正如gcc手册所说:

    If you try to call the nested function through its address after the containing function has exited, all hell will break loose. If you try to call it after a containing scope level has exited, and if it refers to some of the variables that are no longer in scope, you may be lucky, but it's not wise to take the risk. If, however, the nested function does not refer to anything that has gone out of scope, you should be safe.

    简短版本是,您有三个主要选择:

    • 在堆栈上分配闭包,并且在包含函数退出后不允许使用它们。
    • 在堆上分配闭包,并使用某种垃圾收集。
    • 做原创研究,也许从ML,Cyclone等地区的东西开始。

    我明白我已经很晚了,但我偶然偶然发现了这个问题。

    我认为完全支持闭包确实需要GC,但在某些特殊情况下,堆栈分配是安全的。确定这些特殊情况需要进行一些逃逸分析。我建议你看一下BitC语言文章,比如BitC中的Closure Implementation。 (虽然我怀疑这些文件是否反映了当前的计划。)BitC的设计者也遇到了同样的问题。他们决定为编译器实现一种特殊的非收集模式,它拒绝所有可能逃脱的闭包。如果启用,则会显着限制语言。但是,该功能尚未实现。

    我建议你使用收藏家 - 这是最优雅的方式。您还应该考虑一个构建良好的垃圾收集器比malloc更快地分配内存。 BitC人员确实非常重视性能,他们仍然认为即使对于他们的操作系统Coyotos的大多数部分来说,GC都很好。您可以通过简单的方式迁移缺点:

    • 只创造最少量的垃圾
    • 让程序员控制收集器
    • 通过转义分析优化堆栈/堆使用
    • 使用增量或并发收集器
    • 如果可能的话,像Erlang那样划分堆

    许多人担心垃圾收集器因为他们使用Java的经验。 Java有一个很棒的收集器,但是用Java编写的应用程序由于产生了大量的垃圾而存在性能问题。此外,由于启动和响应时间较长,对于桌面应用程序来说,膨胀的运行时和花哨的JIT编译并不是一个好主意。


    这个主题可能会有所帮助,尽管这里的一些答案已经反映了答案。

    一张海报很有用:

    It seems that you want garbage collection for closures
    "in the absence of true garbage collection". Note that
    closures can be used to implement cons cells. So your question
    seem to be about garbage collection"in the absence of true
    garbage collection" -- there is rich related literature.
    Restricting problem to closures does not really change it.

    所以答案是:不,没有优雅的方法来关闭和没有真正的GC。
    你可以做的最好的是一些黑客攻击,以限制你的闭合到特定类型的闭合。如果你有一个合适的GC,这一切都是不必要的。

    所以,我的问题反映了其他一些 - 为什么你不想实现GC?简单的标记+扫描或停止+复制需要大约2-300行(Scheme)代码,并且在编程工作方面并不是那么糟糕。在使程序变慢方面:

  • 您可以实现更复杂的GC,它具有更好的性能。
  • 想想你所用的所有内存泄漏程序都不会受到影响。
  • 使用GC进行编码是一种祝福。 (想想C#,Java,Python,Perl等......与C ++或C相比)。

  • Use reference counting and garbage collect the cycles (I don't really like this)

    设计你的语言是可能的,所以没有周期:如果你只能创造新的对象而不是改变旧的对象,并且如果使对象不能进行循环,则循环永远不会出现。 Erlang基本上以这种方式工作,但在实践中它确实使用GC。


    C ++ 0x规范定义了没有垃圾收集的lambdas。简而言之,在lambda闭包包含不再有效的引用的情况下,规范允许非确定性行为。例如(伪语法):

    1
    2
    3
    4
    5
    6
    (int)=>int create_lambda(int a)
    {
        return { (int x) => x + a }
    }

    create_lambda(5)(4)    // undefined result

    此示例中的lambda引用在堆栈上分配的变量(a)。但是,该堆栈帧已弹出,并且在函数返回后不一定可用。在这种情况下,它可能会工作并返回9作为结果(假设理智的编译器语义),但没有办法保证它。

    如果你正在避免垃圾收集,那么我假设你也允许显式堆与堆栈分配和(可能)指针。如果是这种情况,那么你可以像C ++那样做,只是假设使用你的语言的开发人员足够聪明,可以用lambdas发现问题情况并明确地复制到堆中(就像你在返回一个合成的值时那样)一个功能)。


    如果你有精确复制GC的机器,你可以在堆栈上初始分配并复制到堆并更新指针,如果你在退出时发现指向这个堆栈帧的指针已经转义。这样,只有在实际捕获包含此堆栈帧的闭包时才需要付费。这有助于帮助还是伤害取决于您使用闭包的频率以及捕获的数量。

    您也可以查看C ++ 0x的方法(N1968),尽管人们可能期望从C ++开始,它包括指望程序员指定要复制的内容和引用的内容,如果你弄错了,你就会得到无效的访问。


    So the question: Is there an elegant way to implement closures without resorting to allocate the stack frame on the heap and leave it to garbage collector?

    GC是一般案例的唯一解决方案。


    您可以假设所有闭包最终都会被调用一次。现在,当调用闭包时,您可以在闭包返回时进行清理。

    你打算如何处理返回的物体?它们必须在某些时候进行清理,这与闭包完全相同。


    或者根本不做GC。在某些情况下,最好只是忘记内存泄漏,并在完成后让进程清理完毕。

    根据您对GC的疑虑,您可能会担心定期GC扫描。在这种情况下,当项目超出范围或指针发生变化时,您可以执行选择性GC。我不确定这会有多贵。

    @Allen

    如果在包含函数退出时无法使用它们,那么闭包有什么用呢?从我的理解,这是关闭的全部意义。


    迟到总比不到好?

    您可能会发现这很有趣:差异执行。

    它是一个鲜为人知的控制结构,它的主要用途是编程用户界面,包括可以在使用时动态变化的用户界面。它是模型 - 视图 - 控制器范例的重要替代品。

    我提到它是因为人们可能会认为这样的代码会严重依赖于闭包和垃圾收集,但控制结构的副作用是它消除了这两者,至少在UI代码中是这样。


    我想如果这个过程非常短,这意味着它不能使用太多内存,那么GC是不必要的。这种情况类似于担心堆栈溢出。不要太深,也不能溢出;不要运行太久,你不能需要GC。清理成为简单回收您预先分配的大区域的问题。即使是较长的进程也可以分成较小的进程,这些进程预先分配了自己的堆。例如,这适用于事件处理程序。如果你正在编写编译器,它不能很好地工作;在这种情况下,GC肯定不是一个障碍。


    我已经读过ML的最后版本只能使用GC


    创建多个堆栈?


    推荐阅读

      探探语言设置|探探怎么设置语言

      探探语言设置|探探怎么设置语言,,1. 探探怎么设置语言打开探探软件,然后就有消息提示的红点,点开就行了!其实这些软件都是挺简单的操作的,都是

      git设置编码|git语言设置

      git设置编码|git语言设置,,git设置编码点击cap4j搜索从git直接链接上拉代码。git语言设置Git是一个开源的分布式版本控制系统,可以有效、高

      区域语言设置|区域语言设置工具

      区域语言设置|区域语言设置工具,,区域语言设置工具你好,大致的方法如下,可以参考:1、按下键盘的windows 图标,再开始菜单中单击“设置”;出现的

      c4d语言设置|c4d汉语设置

      c4d语言设置|c4d汉语设置,,1. c4d汉语设置mac版的C4D是这样的,中文字体是有的,但是是以拼音的形式存在,比如黑体就是ht。中文字体以拼音方式

      电脑宣传语|电脑宣传语言

      电脑宣传语|电脑宣传语言,,1. 电脑宣传语言1.我做好了与你过一辈子的打算,也做好了你随时要走的准备,2.每段青春都会苍老,但我希望记忆里的你

      office语言设置|微软office语言设置

      office语言设置|微软office语言设置,,微软office语言设置一、首先点击桌面左下角“WIN键”。二、弹出选项内点击“所有程序”。三、接着点

      小米设置日语|小米设置日语语言

      小米设置日语|小米设置日语语言,,1. 小米设置日语语言MIUI系统文字目前只支持简体中文、繁体中文、英文、藏文和维吾尔文,不支持日文 2. 小

      易语言开发电脑系统|易语言电脑版

      易语言开发电脑系统|易语言电脑版,,1. 易语言电脑版首先编译——是将程序编译为exe文件,只能在有易语言的机子上运行,独立编译——是将程序