Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 垃圾收集器如何从堆栈中查找对象引用?_Haskell_Garbage Collection_Go - Fatal编程技术网

Haskell 垃圾收集器如何从堆栈中查找对象引用?

Haskell 垃圾收集器如何从堆栈中查找对象引用?,haskell,garbage-collection,go,Haskell,Garbage Collection,Go,在Haskell或Go等具有自动垃圾收集功能的语言中,垃圾收集器如何找出堆栈上存储的哪些值是指向内存的指针,哪些值只是数字?如果垃圾收集器只是扫描堆栈并假设所有地址都是对对象的引用,那么许多对象可能会被错误地标记为可访问 显然,可以在每个堆栈帧的顶部添加一个值,该值描述了下一个值中有多少是指针,但这不会降低性能吗 实际上是如何实现的?Haskell堆栈在每个堆栈帧中使用一个内存字(使用位图)描述该堆栈帧中哪些值是指针,哪些值不是指针。有关详细信息,请参阅文章和GHC评注中的文章 公平地说,从各方

在Haskell或Go等具有自动垃圾收集功能的语言中,垃圾收集器如何找出堆栈上存储的哪些值是指向内存的指针,哪些值只是数字?如果垃圾收集器只是扫描堆栈并假设所有地址都是对对象的引用,那么许多对象可能会被错误地标记为可访问

显然,可以在每个堆栈帧的顶部添加一个值,该值描述了下一个值中有多少是指针,但这不会降低性能吗


实际上是如何实现的?

Haskell堆栈在每个堆栈帧中使用一个内存字(使用位图)描述该堆栈帧中哪些值是指针,哪些值不是指针。有关详细信息,请参阅文章和GHC评注中的文章


公平地说,从各方面考虑,一个单词的记忆实际上并没有多少成本。您可以将其视为只向每个方法添加一个变量;这并不是那么糟糕。

一些收集器认为堆栈上的所有内容都是潜在的指针(比如Boehm GC)。事实证明,这并不像人们预期的那么糟糕,但显然是次优的。在托管语言中,通常会在堆栈中留下一些额外的标记信息,以帮助收集器确定指针的位置

请记住,在大多数编译语言中,每次输入函数时堆栈帧的布局都是相同的,因此确保以正确的方式标记数据并不难

“位图”方法是实现这一点的一种方法。位图的每一位对应于堆栈上的一个字。如果位是1,则堆栈上的位置是指针;如果位是0,则从收集器的角度来看,该位置只是一个数字(或沿着这些线的某个位置)。编写得非常好的GHC运行时和调用约定对大多数函数都使用一个字的布局,这样一些位就可以传递堆栈帧的大小,其余的作为位图。较大的堆栈帧需要一个多字结构,但其思想是相同的

关键是开销很低,因为布局信息是在编译时计算的,然后每次调用函数时都包含在堆栈中

更简单的方法是“指针优先”,所有指针都位于堆栈的开头。您只需要在指针前面加上一个长度,或者在指针后面加上一个特殊的“结束”字,就可以知道在这种布局下哪些字是指针

有趣的是,试图将这些管理信息放到堆栈中会产生大量与C的互操作相关的问题。例如,将高级语言编译成C是次优的,因为即使C是可移植的,也很难携带此类信息。为类C语言(GCC、LLVM)设计的优化编译器可能会重新构造堆栈框架,从而产生问题,因此GHC LLVM后端使用自己的“堆栈”,而不是LLVM堆栈,这会导致一些优化。类似地,C代码和“托管”代码之间的边界需要仔细构造,以避免混淆GC


因此,当您在JVM上创建一个新线程时,实际上创建了两个堆栈(一个用于Java,一个用于C)。

存在GC,它假设GC正在管理的某个对象的地址的每个位模式实际上都是一个指针(因此不要释放该对象)。这实际上可以很好地工作,因为调用指针通常比小的公共整数大,并且通常必须对齐。但是,这可能会导致某些对象的收集延迟。Boehm collector for C就是这样工作的,因为它是基于库的,所以没有从编译器获得任何特定的帮助


还有一些gc与所使用的语言更紧密地耦合,并且实际上知道内存中对象的结构。我从未专门阅读过堆栈帧处理,但是如果编译器和GC设计为协同工作,您可以记录信息来帮助GC。一个技巧是将所有指针引用放在一起,并使用每个堆栈帧一个字来记录有多少指针引用,这并不是一个巨大的开销。如果您可以在不添加单词的情况下计算出每个堆栈帧对应的函数,那么您可以在中编译每个函数的“堆栈帧布局图”。另一种选择是使用标记字,将不是指针的字的低阶位设置为1(由于地址对齐),指针永远不需要1,因此可以区分它们。这意味着您必须移动未绑定的值才能使用它们。

重要的是要认识到GHC维护自己的堆栈,而不使用C堆栈(FFI调用除外)。没有可移植的方式来访问C堆栈的所有内容(例如,在SPARC中,它的一些内容隐藏在寄存器窗口中),因此GHC维护一个它可以完全控制的堆栈。维护自己的堆栈后,您可以选择任何方案来区分堆栈上的指针和非指针(如使用位图)。

+1讨论Boehm collector,它使用了一种非常出色的方法。对于某些平台(如IA-64、x86-64)和操作系统(如Linux),您可以使用libunwind()展开堆栈并访问C堆栈帧。这减少了处理C堆栈的一些复杂性(但是积极的编译器优化可能仍然会导致问题)?使用指针优先的方法不是更好吗?似乎LLVM确实有。@fuzzxl,来自GHC评论:“与主要具有“指针优先”布局的堆对象不同,在堆栈框架中,指针和非指针混合在一起。这样我们就可以支持“堆栈存根”,即活变量存储