2015-05-26 102 views
4

我有一个使用更大的索引在过程中创建大型数据结构的scala过程。因为我想要一次完成并且不会在复杂的优先解决方案中变得难以理解,所以在使用表达式初始化的结果中使用lazy vals,该表达式可能无法在创建时评估为正确值(或者任何其他值)该组件,但是一旦整个构建过程完成,就会这样做。这意味着最终结果的每个组成部分都有对整个索引的闭包的合成引用,并且可能只要它们中的任何一个仍在内存中,我的索引就不能被垃圾收集。很显然,我不想要它 - 理想情况下,我希望能够在结构上进行第二遍以便根据需要初始化值(并确保在此处捕获任何错误),并让索引为垃圾收集。目前,我通过名字初始化表达式通过几个功能和懒惰VAL声明中使用它,等价于:Scala:懒惰的vals,按名称调用,关闭和内存泄漏

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

是这种声音?将通过取消引用一次lazy val访问的合成init字段?如果我想在初始化函数中使用它,就像这样:

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

有没有什么规则与封闭编程时保持在一般的想法?

+1

你能稍微加重一点真实的代码吗?如果'index.get'已经是一个返回一个'Component'的函数,那么这个包的好处是什么?此外,哪些类型是可变的(有些必须是,否则没有初始化顺序问题) – Martijn

+0

真正的代码是相当庞大的,但底层的问题是非常基本的:合成闭包垃圾收集后,懒惰的val初始化,如果它只用于该初始化(特殊情况),并有任何规则管理它(通用之一)。在我的情况下,唯一的可变结构是在构建过程中使用的索引,所有其他正向/循环引用都是通过用表达式返回正确值的表达式来初始化的惰性val解析的。闭包的存在是因为初始化表达式可能比较复杂,并且有关于哪些元素不应该关心的论点 – Turin

+0

它不一定是真正的代码 - 甚至是任何真正的代码,只是足够自成一体编译并显示有问题的行为的例子 – Martijn

回答

4

你正在描述的主要问题 - 索引不能被垃圾回收 - 通过将索引放入一个可变框中,一旦创建对象就清空(空),即可解决。然而,如果你不知道知道当你的对象被创建,并且需要程序告诉你(例如通过知道所有懒惰的vals已经被填充),那么你是运气不好的。除非在sun.misc.Unsafe的记忆中徘徊,你不应该知道这些细节。 (这是一个懒惰的瓦尔点。)

你可以做一个引用计数方案,可以帮助你有点在检测自己,当你可以清除方块:当你进入构造函数时增加框上的计数器,保持一个私人领域计数你有多少懒惰vals,并减少计数(原子!)每次当你初始化一个懒惰的val,如果你击中零,减少计数器的方块和空箱子,如果箱子计数器达到零。

+0

如果我需要去这个程度来强制解引用索引,我可以使用一个var作为最终的'lazy'值,另一个var存储要设置的explict init函数在它被评估之后为空,但是它太多了,并且希望它正是编译器在这个例子中生成的代码。 – Turin