gpt4 book ai didi

compiler-construction - 通常如何为闭包实现引用环境?

转载 作者:行者123 更新时间:2023-12-04 00:15:29 24 4
gpt4 key购买 nike

假设我有一个具有深度绑定(bind)的静态/词法范围语言,并且我创建了一个闭包。闭包将包含我要执行的语句以及所谓的引用环境,或者引用 this post , 可以使用的变量的集合。

这个引用环境在实现方面实际上是什么样子的?我最近在阅读有关 ObjectiveC 的 block 实现的内容,作者建议您在幕后获取堆栈上所有变量的副本以及对堆对象的所有引用。该解释声称您在创建闭包的时间点获得了引用环境的“快照”。

  1. 这或多或少会发生什么,还是我误读了?
  2. 是否做了任何事情来“卡住”堆对象的单独副本,或者是否可以安全地假设如果它们在闭包创建和闭包执行之间被修改,闭包将不再在原始版本上运行对象?
  3. 如果确实存在复制,在可能想要创建大量闭包并将它们存储在某处的情况下是否需要考虑内存使用情况?

我认为对其中一些概念的误解可能会导致棘手的问题,例如 Eric Lippert 在 this blog post 中提到的问题.这很有趣,因为您认为保留对调用闭包时可能消失的值类型的引用是没有意义的,但我猜测在 C# 中,编译器会找出变量稍后需要并将其放入堆中。

似乎在大多数内存管理语言中,一切都是引用,因此 ObjectiveC 在某种程度上是一种独特的情况,它必须处理复制堆栈上的内容。

最佳答案

这是一个类似 javascript 语法的运行示例。

function f(x) {
var y = ...;
function g(z) {
function h(w) {
.... y, z, w ....
}
.... x, h ....
}
.... x, g ....
}

一种表示是环境的链接链。也就是说,一个闭包由一个代码指针、一些槽和对封闭闭包或顶层环境的引用组成。在这个表示中,

f = [<code>, <top-level-env>]
g = [<code>, f, x, y]
h = [<code>, g, z]

除了有时让每个函数都直接引用顶层环境会更好,因为它经常被使用:

f = [<code>, <top-level-env>]
g = [<code>, <top-level-env>, f, x, y]
h = [<code>, <top-level-env>, g, z]

(还有其他变体。)

这种表示的一个优点是您可以将可变变量直接存储在闭包中。 (好吧,也许吧,这取决于你如何表示函数激活。)一个缺点是,如果你有深层嵌套的闭包,一些变量可能需要多次跳跃才能到达。另一个缺点是,如果闭包比其父闭包生命周期长(例如,g 返回 h),则此表示可能会阻止 GC 收集大部分甚至完全无法访问的环境帧。

另一种表示是“平面闭包”:每个闭包都包含一个代码指针和用于所有代码自由变量的槽。

g = [<code>, x, y]
h = [<code>, y, z]

此表示修复了空间/GC 问题;没有闭包在内存中固定另一个闭包。另一方面,自由变量槽被复制而不是共享,所以如果有一个包含许多自由变量的嵌套闭包——或者嵌套闭包的许多实例——整体内存使用量可能更高。此外,这种表示通常需要为可变变量存储以堆分配(但仅适用于实际发生变异的变量,并且仅当变异无法自动重写时)。

还有混合方法。例如,您可能拥有大部分平坦的闭包,但会特别对待顶层环境:

g = [<code>, <top-level-env>, x, y]

或者您可能有一个“足够聪明”(或者至少“足够雄心勃勃”)的编译器,它会尝试根据自由变量的数量、嵌套深度等在表示之间进行选择。

关于compiler-construction - 通常如何为闭包实现引用环境?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12233155/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com