缓存反射数据 [英] Caching reflection data

查看:26
本文介绍了缓存反射数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

缓存从反射获得的昂贵数据的最佳方法是什么?例如,大多数快速序列化程序缓存此类信息,因此它们不需要在每次再次遇到相同类型时进行反映.他们甚至可能生成一个动态方法,从类型中查找.

What's the best way to cache expensive data obtained from reflection? For example most fast serializers cache such information so they don't need to reflect every time they encounter the same type again. They might even generate a dynamic method which they look up from the type.

传统上,我为此使用了普通的静态字典.例如:

Traditionally I've used a normal static dictionary for that. For example:

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
    }
} 

这会泄漏一点内存,但由于它每个类型只泄漏一次,并且类型只要 AppDomain 存在,我不认为这是一个问题.

This leaks a bit of memory, but since it does that only once per Type and types lived as long as the AppDomain I didn't consider that a problem.

但现在 .net 4 引入了 动态类型的可收集程序集一代.如果我曾经在可收集程序集中声明的对象上使用 DoSomething,该程序集将永远不会被卸载.哎哟.

But now .net 4 introduced Collectible Assemblies for Dynamic Type Generation. If I ever used DoSomething on an object declared in the collectible assembly that assembly won't ever get unloaded. Ouch.

那么,在 .net 4 中缓存每种类型信息的最佳方法是什么,而不会遇到此问题?我能想到的最简单的解决方案是:

So what's the best way to cache per type information in .net 4 that doesn't suffer from this problem? The easiest solution I can think of is a:

private static ConcurrentDictionary<WeakReference, TCachedData> cache.

但是 IEqualityComparer<T> 我必须使用它会表现得非常奇怪并且可能也会违反合同.我也不确定查找的速度有多快.

But the IEqualityComparer<T> I'd have to use with that would behave very strangely and would probably violate the contract too. I'm not sure how fast the lookup would be either.

另一个想法是使用过期超时.可能是最简单的解决方案,但感觉有点不雅.

Another idea is to use an expiration timeout. Might be the simplest solution, but feels a bit inelegant.

在类型作为泛型参数提供的情况下,我可以使用嵌套泛型类,该类不应该受此问题的影响.但是如果类型是在变量中提供的,他就不起作用.

In the cases where the type is supplied as generic parameter I can use a nested generic class which should not suffer from this problem. But his doesn't work if the type is supplied in a variable.

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.
    }
}

<小时>

更新:我刚刚想到的一个想法是使用 Type.AssemblyQualifiedName 作为键.这应该唯一标识该类型而不将其保留在内存中.我什至可能会在这个字符串上使用引用标识.


Update: One idea I've just had is using Type.AssemblyQualifiedName as the key. That should uniquely identify that type without keeping it in memory. I might even get away with using referential identity on this string.

这个解决方案仍然存在的一个问题是缓存的值也可能保留对类型的引用.如果我为此使用弱引用,它很可能会在程序集卸载之前到期.而且我不确定从弱引用中获取正常引用的成本有多低.看起来我需要做一些测试和基准测试.

One problem that remains with this solution is that the cached value might keep a reference to the type too. And if I use a weak reference for that it will most likely expire far before the assembly gets unloaded. And I'm not sure how cheap it is to Get a normal reference out of a weak reference. Looks like I need to do some testing and benchmarking.

推荐答案

ConcurrentDictionary 在这种情况下是不正确的.假设我们试图缓存类型 T 的信息,所以 WeakReference.Target==typeof(T).CachedData 也很可能包含对 typeof(T) 的引用.由于 ConcurrentDictionary 将项目存储在 Node 的内部集合中,您将拥有强引用链:ConcurrentDictionary实例 -> Node 实例 -> Value 属性(CachedData 实例)-> typeof(T).一般来说,在 Values 可以引用它们的 Keys 的情况下,使用 Wea​​kReference 不可能避免内存泄漏.

ConcurrentDictionary<WeakReference, CachedData> is incorrect in this case. Suppose we are trying to cache info for type T, so WeakReference.Target==typeof(T). CachedData most likely will contain reference for typeof(T) also. As ConcurrentDictionary<TKey, TValue> stores items in the internal collection of Node<TKey, TValue> you will have chain of strong references: ConcurrentDictionary instance -> Node instance -> Value property (CachedData instance) -> typeof(T). In general it is impossible to avoid memory leak with WeakReference in the case when Values could have references to their Keys.

有必要添加对ephemeron 的支持,以便在不发生内存泄漏的情况下实现这种情况.幸运的是 .NET 4.0 支持它们,我们有 ConditionalWeakTable类.看来 原因 介绍它与您的任务很接近.

It was necessary to add support for ephemerons to make such scenario possible without memory leaks. Fortunately .NET 4.0 supports them and we have ConditionalWeakTable<TKey, TValue> class. It seems the reasons to introduce it are close to your task.

这种方法还解决了您更新中提到的问题,因为只要加载了程序集,对 Type 的引用就会一直存在.

This approach also solves problem mentioned in your update as reference to Type will live exactly as long as Assembly is loaded.

这篇关于缓存反射数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆