2016-05-21 42 views
5

我正在从头开始编写一个简单的lisp解释器。我有一个全局环境,在评估文件中的所有表单时,顶级变量都会被绑定。当文件中的所有表单都被评估过时,顶层env和其中的所有键值数据结构都被释放。何时在lisp解释器中释放闭包的内存

当评估者遇到lambda表单时,它会创建一个包含3件东西的PROC对象:应用过程时要在本地框架中绑定的参数列表,该函数的主体以及指向环境它是在创建,例如:

​​3210

将产生什么样的内部:

PROC- args: x, 
     body: x, 
     env: pointer to top level env 

当施加PROC中,为帧和第创建了一个新的环境本地绑定将在那里进行,以允许使用适当的绑定来评估身体。这个框架环境包含一个指向它的闭包的指针,以允许在THAT中进行变量查找。在这种情况下,这将是全球环境。在对PROC正文进行评估后,我可以释放与其关联的所有单元格,包括其框架环境,并退出时不会发生内存泄漏。

我的问题是高阶函数。试想一下:

(define conser 
    (lambda (x) 
     (lambda (y) (cons x y)))) 

它有一个参数,并产生另一个函数的函数,将利弊这样的说法你进入它的东西。所以,

(define aconser (conser '(1))) 

将产生该cons'es '(1)到任何被传递到它的功能。例如:

(aconser '(2)) ; ((1) 2) 

我在这里的问题是,aconser必须保留一个指向其创建的环境中,即中conser时通过调用(conser '(1))制作。当aconser应用PROC时,其帧必须指向定义为aconser时存在的conser的帧,因此在应用它之后我不能释放conser的帧。我不知道在应用时如何释放与lambda框架相关联的内存,并且还支持这种持久高阶函数。

我能想到的一些解决方案的:

  • 某种类型的ARC

  • 的复制封闭环境到评价PROC的帧时,它产生

这似乎暗示here。因此,我不会将一个指针保存在PROC对象中,而是将它关闭,我会......复制闭包环境,并将指针直接存储在单元格中的?这不仅仅是踢一个更深的水平,导致同样的问题?

  • 递归代高阶函数体内部在读取时间标签

我担心我可能失去了一些东西很简单,我也很好奇,怎么这个程序在其他lisp语言和其他通常使用闭包的语言中得到支持。我没有太多的运气来寻找答案,因为问题是非常具体的,甚至可能是这个实现(我承认我只是作为一个学习项目脱离了我的帽子),而我能找到的大部分内容只是解释了具体情况从语言实施的角度来看,而不是语言正在实施的语言。

Here is a link到我的来源的相关行,如果它是有用的,我很高兴阐述,如果这个问题不够详细,足以描述问题彻底。谢谢!

+1

我可能会遗漏一些东西,但是你说:“在评估PROC体之后,我可以释放与它相关的所有单元格,包括它的框架环境,并退出时没有内存泄漏。”。你不能多次重复使用闭包吗?在这种情况下,您将过早释放环境。 – coredump

+0

这正是问题所在。我在这里区分了闭包(无论env是一个lambda是否已经被eval了,导致一个proc)和框架,这是一个proc内部变量在应用时的本地绑定。我可能会误解这些术语,虽然... – jfo

+0

因此,如果我有一个只有单层深度的lambda表达式,每次应用它时都会重新创建它的框架,并使用传递给它的任何参数。 – jfo

回答

1

这种处理方式通常在天真的口译员中是使用垃圾收集器(GC)并在GC堆中分配激活帧。所以你从不明确地释放这些帧,你可以让GC在适用的时候释放它们。

在更复杂的实现,你可以用一个稍微不同的方法:

  • 时创建一个封闭,不存储指向当前的环境。相反,复制闭包使用的那些变量的值(它被称为lambda的自由变量)。 并将封闭体更改为使用这些副本,而不是在环境中查找这些变量。它被称为关闭转换
  • 现在您可以将您的环境视为普通堆栈,并在您退出示波器后立即释放激活帧。
  • 你仍然需要一个GC来决定什么时候关闭关闭可以被释放。
  • 这反过来需要一个“赋值转换”:复制变量的值意味着如果这些变量被修改,语义的变化。因此,要恢复原始语义,您需要查找那些“复制到闭包”以及“修改”的变量,并将它们变为“参考单元”(例如缺陷单元格,您可以在其中保留值car),以便副本不会复制该值,而只是将引用复制到保存值的实际位置。 [注意:这样的实现显然意味着避免使用setq并使用更实用的风格可能会更有效率。 ]

更为复杂的实现也具有优势,它可以为空间语义提供了一种安全:封闭只会留住数据,它实际上是指,违背了幼稚的方法,即关闭结束指的是整个周围的环境,因此可以阻止GC收集实际未被引用的数据,但恰好在封闭捕获时发生在环境中。

+0

ty,这是一个彻底的答案,基本上反映了我的问题后研究显示了我。对于我的玩具lisp,我决定GC不在我原来的项目范围内,所以我没有实现它,但我计划在我的下一次尝试:) – jfo

+0

你可以阅读关于我在[这里]结束了什么( http://blog.jfo.click/sild-is-a-lisp-dialect/)。 – jfo