Haskell没有显式的内存管理,所有对象都是按值传递的,因此没有明显的引用计数或垃圾回收。 Haskell编译器通常如何决定为给定变量生成在堆栈上分配的代码还是在堆上分配的代码? 它是否会在相同函数的不同调用站点中始终堆或栈分配相同的变量? 在分配时,它如何决定何时释放内存? 栈分配和解除分配是否仍按C语言中相同的函数入口/出口模式执行?
Haskell没有显式的内存管理,所有对象都是按值传递的,因此没有明显的引用计数或垃圾回收。 Haskell编译器通常如何决定为给定变量生成在堆栈上分配的代码还是在堆上分配的代码? 它是否会在相同函数的不同调用站点中始终堆或栈分配相同的变量? 在分配时,它如何决定何时释放内存? 栈分配和解除分配是否仍按C语言中相同的函数入口/出口模式执行?
当您像这样调用一个函数时
f 42 (g x y)
那么运行时的行为大致如下:
p1 = malloc(2 * sizeof(Word))
p1[0] = &Tag_for_Int
p1[1] = 42
p2 = malloc(3 * sizeof(Word))
p2[0] = &Code_for_g_x_y
p2[1] = x
p2[2] = y
f(p1, p2)
也就是说,参数通常作为指向堆上对象的指针传递,就像Java中一样,但与Java不同的是,这些对象可能代表挂起的计算,即所谓的“thunks”,例如在我们的例子中的(g x y
/ p2
)。如果没有优化,这种执行模型效率非常低,但有方法可以避免许多这些开销。
42
而不是创建堆对象p1
。Char
和Int
)被缓存。即,返回对缓存对象的指针,而不是为每个对象分配一个新指针。即使对象最初在堆上分配,垃圾收集器以后也会去重它们(仅限于小的Int
和Char
)。由于对象是不可变的,因此这是安全的。x
没有在其他地方使用,那么优化 x ++ y
以避免复制 x
(只需将最后的尾指针更改为指向 y
)是否可能? GHC 是否会这样做? - Roman Cheplyakalet
绑定。没有进行任何过程间分析。对于您的示例,您需要静态确保堆中没有其他指针指向x
或其任何后继节点。您需要线性或唯一类型来证明这样的事情。 - nominoloGHC只在堆栈上放置评估上下文。任何使用let/where绑定分配的东西以及所有数据构造函数和函数都存储在堆中。惰性求值使得你所知道的关于严格语言执行策略的一切都变得无关紧要。