2015-05-26 35 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