缓存从反射获得的昂贵数据的最佳方法是什么?例如,大多数快速序列化器会缓存此类信息,因此它们不必在每次再次遇到相同类型时都进行反映。他们甚至可能生成一种动态方法,并从类型中查找。
传统上,我为此使用了一个普通的静态字典。例如:
private static ConcurrentDictionary<Type, Action<object>> cache; public static DoSomething(object o) { Action<object> action; if(cache.TryGetValue(o.GetType(), out action)) //Simple lookup, fast! { action(o); } else { // Do reflection to get the action // slow } }
这会泄漏一些内存,但是由于它只对每个Type执行一次,并且只要
AppDomain
存在,Type就不会存在问题。
但是现在.net 4引入了Collectible Assemblies for Dynamic Type Generation。如果我曾经在可收集程序集中声明的对象上使用过DoSomething
,则该程序集将永远不会被卸载。哎哟
因此,在不受此问题困扰的.net 4中缓存每种类型信息的最佳方法是什么?我能想到的最简单的解决方案是:
private static ConcurrentDictionary<WeakReference, TCachedData> cache.
但是我必须使用的
IEqualityComparer<T>
的行为会很奇怪,并且可能也会违反合同。我不确定查询的速度有多快。
另一个想法是使用过期超时。可能是最简单的解决方案,但感觉不太优雅。
在将类型作为通用参数提供的情况下,我可以使用嵌套的通用类,该类不应受到此问题的困扰。但是,如果类型是在变量中提供的,则他不起作用。
:我刚刚想到的一个想法是使用class MyReflection { internal Cache<T> { internal static TData data; } void DoSomething<T>() { DoSomethingWithData(Cache<T>.data); //Obviously simplified, should have similar creation logic to the previous code. } }
Update
Type.AssemblyQualifiedName
作为键。那应该唯一地标识该类型而不将其保留在内存中。我什至可能不会在此字符串上使用引用身份。此解决方案仍然存在的一个问题是,缓存的值可能还会保留对该类型的引用。而且,如果我为此使用弱引用,则它很可能在卸载程序集之前就过期了。我不确定从弱引用中获取普通引用有多便宜。看来我需要做一些测试和基准测试。
缓存从反射获得的昂贵数据的最佳方法是什么?例如,大多数快速的序列化程序会缓存此类信息,因此无需在每次遇到相同类型时都进行反映...
ConcurrentDictionary<WeakReference, CachedData>
在这种情况下不正确。假设我们正在尝试缓存类型T的信息,因此WeakReference.Target==typeof(T)
。 CachedData最有可能还将包含typeof(T)
的引用。当ConcurrentDictionary<TKey, TValue>
将项目存储在Node<TKey, TValue>
的内部集合中时,您将具有强引用链:ConcurrentDictionary
实例-> Node
实例-> Value
属性(CachedData
实例)-> typeof(T)
。通常,在Value可以引用其Key的情况下,使用WeakReference避免内存泄漏是不可能的。
您应该签出reasons库
我可能会在这里说明显而易见的内容,但是: