我有一个 scala 过程,在此过程中使用更大的索引创建了一个大型数据结构。因为我想一次性完成并且不会陷入复杂的优先级解析中,所以我在用表达式初始化的结果中使用了惰性 vals,这些表达式在创建时可能不会评估为正确的值(或任何值)组件,但会在整个构建过程完成后执行。这意味着最终结果的每个组件都有一个对包含我的整个索引的闭包的合成引用,并且可能,只要它们中的任何一个仍在内存中,我的索引就不能被垃圾收集。显然,我不想要它 - 理想情况下,我希望能够对结构进行第二次传递以在需要时初始化值(并确保此时捕获任何错误),并让索引为垃圾收集。目前我通过几个函数按名称传递初始化表达式,并在惰性 val 声明中使用它,相当于:

class Component(init : =>Component) {
   lazy val property = init
}
...
new Component(index.get(parameters))

这是声音吗?一旦访问惰性 val,合成 init 字段是否会被取消引用?如果我想在初始化函数中使用它怎么办,像这样:
class Component(init : =>Component) {
   private def evaluate = init
   lazy val property = evaluate
}

在使用闭包进行编程时,是否有任何要记住的一般规则?

最佳答案

您所描述的主要问题——索引不能被垃圾收集——是通过将索引放入一个可变框来解决的,一旦对象被创建,你就会清空(空出)该框。

但是,如果您不知道您的对象何时创建,并且需要程序告诉您(例如,通过知道所有惰性 val 已被填充),那么您就不走运了。除非用 sun.misc.Unsafe 在内存中翻来覆去,否则你不应该知道这些细节。 (这就是惰性 vals 的重点。)

你可以制定一个引用计数方案,当你可以清除盒子时,它会帮助你在一定程度上检测自己:当你进入构造函数时增加盒子上的计数器,保持你有多少惰性值的私有(private)字段计数,以及每次初始化惰性值时(原子地!)减少计数,如果您达到零,则减少盒子上的计数器并在盒子计数器达到零时将盒子归零。

关于Scala:惰性值、按名称调用、闭包和内存泄漏,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/30462751/

10-11 16:36