2013-03-25 114 views
17

Hash-consing包含在內存中保留給定對象的一個​​副本;也就是說,如果兩個對象在語義上相同(相同的內容),那麼它們應該在物理上相同(存儲器中的相同位置)。該技術通常通過保持全局哈希集並僅在不等於哈希集中的對象時創建新對象來實現。F#中的哈希鏈接和.net中的弱哈希表

另一個要求是散列表中的對象應該是可收集的,如果它們沒有被散列表以外的任何東西引用;否則表示,散列表應該包含弱引用。

這個問題更復雜的是需要有恆定的時間,因此淺,哈希和平等測試;因此對象具有一個唯一的標識符,當將新對象添加到表中時,該標識符會增加。

我有一個使用System.Collections.Generic.Dictionary<key, node>的工作實現,其中key是一個給出節點的淺層摘要(適用於默認散列和相等測試)的元組,並且node是對象。唯一的問題是Dictionary保持對節點的強引用!

我可以使用DictionaryWeakReference's,但這不會釋放指向懸空引用的密鑰。

一些倡導者使用System.Runtime.CompilerServices.ConditionalWeakTable,但這個類似乎做了相反的事情:它在收集密鑰時釋放值,而在收集值時需要釋放密鑰。

一個可以嘗試使用System.Runtime.CompilerServices.ConditionalWeakTable<node, node>,但我需要自定義的散列和平等的測試...和ConditionalWeakTable是記錄使用GetHashCode()虛方法,而不是使用默認的散列函數。

因此,我的問題:是否有一些相當於Dictionary,將保持對值的弱引用,並且當引用變成懸掛時釋放密鑰?

+0

您是否需要在收集該值時立即釋放密鑰?或者你能否放鬆這個要求,並在稍後的某個時間點解鎖? – 2013-03-25 14:27:45

+0

我不需要他們立即被釋放 - 這只是我不希望他們積累和無用地消耗大量的內存。我想過運行另一個線程來週期性地殺死具有懸掛引用的鍵,但這看起來很複雜並且容易出現併發錯誤。 – 2013-03-25 14:34:05

+0

對於它的價值,我也有一個OCaml實現,使用'Weak'模塊的哈希表和一個Java實現'WeakHashMap'。 – 2013-03-25 15:32:47

回答

3

你說得對,CWT並沒有解決散列問題,因爲它引發了問題 - 它的關鍵是假設引用相等。但是,值得指出的是,CWT並不支持鍵或值。這裏是一個小測試:

open System.Collections.Generic 
open System.Runtime.CompilerServices 

let big() = 
    ref (Array.zeroCreate (1024 * 1024) : byte []) 

let test1() = 
    let d = Dictionary(HashIdentity.Reference) 
    for i in 1 .. 10000 do 
     stdout.WriteLine(i) 
     let big = big() 
     d.Add(big, big) 
    d 

let test2() = 
    let d = ConditionalWeakTable() 
    for i in 1 .. 10000 do 
     stdout.WriteLine(i) 
     let big = big() 
     d.Add(big, big) 
    d 

在我的機器,test1運行的內存和test2成功。看起來只有當CWT沒有堅持關鍵和價值時纔會發生這種情況。

對於hash-consing,您最好的選擇可能是Artem在評論中提出的建議。如果這聽起來過於複雜,這也使得有很大的意義,只是給用戶控制,說:

let f = MyFactory() // a dictionary with weak reference values hidden inside 
f.Create(..) : MyObject // MyObject has no constructors of its own 
f.Cleanup() // explicitly cleans up entries for collected keys 

那你還不需要引入線程,研究如何內部工作GC,或做任何魔法。圖書館的用戶可以決定清理的地方,或者簡單地「忘記」工廠對象 - 這將收集整個表格。

+1

我嘗試使用CWT,但它似乎立即收集數據放入表中(因爲一旦密鑰變得無法訪問,就會收集該值)。你有沒有嘗試從CWT恢復數據?使用從A到A的CWT是不可能的,因爲CWT不使用數據類型的哈希碼函數,而是調用默認的哈希函數,這不適用於哈希函數(需要使用唯一標識符的淺哈希)。一種解決方案是複製CWT源代碼並對其進行調整。 – 2013-03-28 09:44:21

+0

@monniaux:是的,我同意CWT不適合哈希計算。 OCaml的弱勢表在這裏很明顯。從CWT中恢復數據是好的,但如果你堅持按鍵 - 這是它的設計目的。是的,如果你找到一個好的解決方案或寫你自己的 - 發佈哈希。 – t0yv0 2013-03-28 12:06:40