原文:C# Tips & Tricks: Weak References - When and How to Use Them
Sometimes you have an object which is very large and needed multiple times, but not constantly, throughout your application. For example a huge lookup table, or the contents of a large file you need in memory for some algorithm. To work with this object, you traditionally have two options:
- As a local variable inside the method that needs it
- As a field in a class that lives during the whole time the object could be needed
有时候,你有一个很大的对象,多次但又不经常需要它,且遍布整个应用。比如一个庞大的查找表或者一个大文件的内容存于内存中用于一些算法。传统地,要使用这个对象,你有两种选择:
- 作为一个局部变量内置于需要它的方法中
- 作为一个字段存在于一个类中,存活于需要该对象的整个时间周期中
Both solutions aren't optimal if your object is very huge, and only sometimes needed:
如果你的对象十分大,并且只有有些时候需要,那么以上两种方法都不是最理想的:
The class field solution keeps the object in memory the whole time, giving your application a huge permanent memory footprint increase. And the local variable solution will decrease application performance, since the object not only has to be created anew every time it's needed, but also deleted each time it goes out of scope, generating work for the Garbage Collector.
类字段的方案整个时间周期保持对象于内存中,造成内存持久占用。局部变量方案会降低应用的性能,因为不仅要在每次需要它的时候被重新创建,而且在每次超出作用域的时候删除它,给垃圾回收增加了工作量。
If the creation of the object is very expensive, and you want to avoid doing it multiple times, the class field solution is your way to go. But in all cases where the object is a cheap-to-create memory hog, a better solution than both of the above would be welcome.
如果创建这个对象代价非常高,你想避免多次这样操作,类字段的方法是你会采用的。但是当这个对象可以被廉价创建,一种较之于以上两种方案更好的方案将会更受欢迎。
Luckily for us, .Net 4.0 gives us a middle-ground solution in the form of weak references.
幸运的是,.Net 4.0以弱引用的形式给了我们一个折衷的方案。
Usually, when an object goes out of scope, or is set to null, it is no longer accessible to us, even if the garbage collector will only delete the object much later.
通常,当一个对象超出作用域或者被置空,对于我们它将不再能被访问,尽管垃圾回收会在很久以后删除这个对象。
A weak reference object will keep a reference to an object that went out of scope or was set to null, until it is actually deleted by the garbage collector. But until that happens, we can get the object back!
一个弱引用对象将会保持一个指向一个超出作用域或者被置空的对象的引用,直到它确实被垃圾回收删除。直到被删除前,我们一直可以把该对象取回来。
So our new option to work with a large object is to use a weak reference to it. The weak reference itself should become a long-living class member. And in the method that needs our huge object, we check if the weak reference holds an object, which we will use if it does. And if not, we create a new instance.
于是我们对于一个较大对象的选择是使用一个弱引用指向它。这个弱引用本身将成为一个长期存活的类成员。在需要我们的大对象的方法中,我们检查这个弱引用是否持有一个对象,如果是则我们将会使用它。如果不是,我们创建一个新实例。
This way, when we first run our method, a new instance of the huge object is created. But when we run it the next time, and the garbage collector hasn't deleted the object in the meantime, we can reuse it, and don't need to create a new instance.
这样,当我们第一次运行我们的方法的时候,这个大对象的一个新实例会被创建。但当我们下次运行它时,与此同时垃圾回收还没有删除找个对象,我们可以重用它,而不需要创建一个新的实例。
The actual usage is pretty simple.
实际用法很简单。
To create a weak reference to an object, we simply call the constructor with the object:
为了创建一个指向一个对象的弱引用,我们简单的的调用这个构造器并传入这个对象:
WeakReference w = new WeakReference(MyHugeObject);
To get the underlying object from a weak reference, we use its .Target property:
为了从一个弱引用中得到这个对象,我们使用 its.Target 属性:
MyHugeObject = w.Target as MyHugeClass;
If the object still exists, it is returned. If it was claimed by the garbage collector, a null refence is returned by the 'as' operator.
如果这个对象一直存在,他会被返回。如果被垃圾回收了,会通过 'as' 操作符返回一个空引用。
So, given that we have a WeakReference w as a field in our class, we would use it inside the method that does something with a HugeObject like this:
于是,在我们的类中有一个 WeakReference 类型的变量 w 作为一个字段,我们将在这个方法里使用它结合一个 HugeObject 来做一些事情,像这样:
static void Func()
{
MyHugeClass MyHugeObject;
if ( (w == null) || ( (MyHugeObject=w.Target as MyHugeClass) == null) )
{
MyHugeObject = new MyHugeClass();
w = new WeakReference(MyHugeObject);
}
// work with MyHugeObject
}
First we declare the MyHugeObject variable, which will be initialized to null by the compiler. Then we check if our weak refence is null (which is the case when Func runs for the very first time) or if the assignment from w.Target returned null. If either is the case, we (re)create MyHugeObject and assign it to w, after which we can then do some work with MyHugeObject.
首先我们声明了这个 MyHugeObject 类型的变量,将会被编译器初始化为 null 。然后我们检查我们的弱引用是否为空(就是在 Func 第一次运行的情况)或者 w.Target 为空。如果满足任何一种情况,我们创建 MyHugeObject 并把它赋值给 w,之后,我们可以用 MyHugeObject 来做些事情。
Now, WeakReference does us one huge favor: it takes care of all members of MyHugeObject, and makes sure we only get it back if all its members are still intact, so we don't need to worry that we get back an incomplete object.
现在,WeakReference 带给我们一个好处:它负责 MyHugeObject 的所有成员,并确保只有在它所有成员完整无缺的情况下我们才能取回它,因此我们不需要担心取回一个不完整的对象。
A drawback is that WeakReference doesn't play well with objects that implement IDisposable, since we cannot call Dispose() (or have it called automatically through the 'using' statement) through a WeakRefence to the object.
WeakReference 的一个缺点是不能很好地和实现 IDisposable 接口的对象协作,是因为我们不能通过一个指向该对象的 WeakRefence 来调用 Dispose()(或者通过 'using' 语句自动使它被调用)。
And finally a word of warning: weak references aren't a guaranteed profit for application performance. In most cases, they will make an algorithm more performant than when using very large local variables. But it's not guaranteed, and in some cases it could produce noticeable overhead (for example when the huge object is a data structure consisting of many smaller objects with references to each other, and a WeakRefence to the data structure turns all those internal refernces to weak references, this might incur garbage collector overhead, because every reference has to be checked to decide if the object as a whole can be recovered or deleted).
最后一个警告:弱引用不能保证为应用的性能带来好处。在大多数情况下,它们会使算法更高性能的,较之于使用一个大局部变量。但这并不能被保证,且在有些情况下会引起明显的开销(比如,当这个大对象是一个由许多较小的之间互相引用的对象构成的数据结构,一个 WeakRefence 指向这个数据结构会把所有的内部引用转变成弱引用,这可能会引起垃圾回收器的开销,因为每一个引用必须被检查来确定是否这个对象可以被恢复或者被删除)。
So the best advice with weak references is: profile or benchmark it, to make sure that you choose the best solution for your specific situation.
所以,对于弱引用的最好的建议是:概要描述或者用基准问题测试它,以确保你为特定的环境选择最好的方案。