2013-03-25 157 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