简体   繁体   English

Scala:懒惰的vals,按名称调用,闭包和内存泄漏

[英]Scala: lazy vals, call by name, closures and memory leaks

I have a scala procedure creating a large data structure using an even larger index in the process. 我有一个scala过程,在过程中使用更大的索引创建一个大型数据结构。 Because I want to do it in one pass and not get boggled down in complicated precedence resolution, I'm using lazy vals in the result initialized with expressions which might not evaluate to a correct value (or any at all) at the moment of creating the component, but will do so once the whole build process is completed. 因为我想在一次传递中完成并且不会在复杂的优先级解析中被忽略,所以我在使用表达式初始化的结果中使用延迟val,这些表达式在创建时可能无法评估为正确的值(或任何值)组件,但一旦整个构建过程完成,它将这样做。 This means that each component of the final result has a synthetic reference to a closure with my whole index, and potentially, as long as any of them is still in memory, my index cannot be garbage collected. 这意味着最终结果的每个组件都有一个对我的整个索引的闭包的合成引用,并且可能,只要它们中的任何一个仍在内存中,我的索引就不能被垃圾收集。 Obviously, I don't want it - ideally I'd like to be able to make a second pass over the structure to initialize the values if needed (and to ensure that any errors are caught at this point), and let the index be garbage collected. 显然,我不想要它 - 理想情况下我希望能够在结构上进行第二次传递以在需要时初始化值(并确保此时捕获任何错误),并让索引为垃圾收集。 Currently I pass the the initialization expression by name through several functions and use it in a lazy val declaration, equivalent to this: 目前我通过几个函数按名称传递初始化表达式,并在lazy val声明中使用它,相当于:

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

Is this sound? 这听起来不错? Will the synthetic init field by dereferenced once lazy val is accessed? 一旦访问了懒惰的val,是否会解除引用合成的init字段? What if I want to use it in an initialization function, like this: 如果我想在初始化函数中使用它,如下所示:

class Component(init : =>Component) {
   private def evaluate = init
   lazy val property = evaluate
}

Are there any rules to keep in mind in general when programming with closures? 在使用闭包进行编程时,是否有任何规则要记住?

The main problem you're describing--that the index can't be garbage collected--is solved by putting the index into a mutable box that you empty (null out) once the object is created. 您正在描述的主要问题 - 索引不能被垃圾收集 - 通过将索引放入一个可变框中来解决,该框在创建对象后清空(null)。

However, if you don't know when your object is created, and need the program to tell you (eg by knowing that all lazy vals have been populated), then you're out of luck. 但是,如果您不知道对象何时被创建,并且需要程序告诉您(例如,通过知道已经填充了所有惰性值),那么您运气不好。 Barring poking around in memory with sun.misc.Unsafe , you're not supposed to know those sorts of details. 除非在sun.misc.Unsafe记忆中sun.misc.Unsafe ,你不应该知道那些细节。 (That's kind of the point of lazy vals.) (这是懒惰的一点。)

You could cook up a reference-counting scheme that would help you somewhat at detecting yourself when you can clear the box: increment a counter on the box when you enter the constructor, keep a private field count of how many lazy vals you have, and decrement that count (atomically!) every time you initialize a lazy val and if you hit zero, decrement the counter on the box and null the box if the box counter has reached zero. 你可以编写一个引用计数方案,它可以帮助你在清除方框时检测自己:在你输入构造函数时在框上增加一个计数器,保持一个私有字段计数你有多少懒惰值,以及每次初始化一个惰性值时递减计数(原子!),如果你达到零,则递减计数器上的计数器,如果盒计数器达到零,则为空。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM