在C#中,弱引用(Weak Reference)是对一个对象的引用,它不会阻止系统垃圾回收器回收这个对象。当垃圾回收器运行时,如果一个对象只被弱引用指向,那么这个对象可以被回收以释放内存。如果应用程序的代码可以访问一个正由该程序使用的对象,垃圾回收器就不能回收该对象, 那么,就认为应用程序对该对象具有强引用。弱引用允许应用程序访问对象,同时也允许垃圾回收器收集相应的对象。如果不存在强引用,则弱引用的有限期只限于收集对象前的一个不确定的时间段。使用弱引用时,应用程序仍可对该对象进行强引用,这样做可防止该对象被收集。但始终存在这样的风险:垃圾回收器在重新建立强引用之前先处理该对象。
占用大量内存,但通过垃圾回收功能回收以后很容易重新创建的对象特别适合使用弱引用。
优点:
缺点:
尽管弱引用有其用途,但在大多数情况下,你可能不需要直接使用它们。只有在设计大型数据结构或缓存,或者在其他需要精细控制对象生命周期的场景中,才需要考虑使用弱引用。
弱引用最好在你确实需要控制内存使用或管理复杂的对象生命周期时才使用。错误的使用弱引用可能会导致难以调试的问题,因为弱引用的目标对象可能在任何时间被自动删除。
创建弱引用:在C#中,可以通过WeakReference
类来创建一个弱引用。例如:
// 创建强引用对象
object obj = new object();
// 创建弱引用
WeakReference weakRef = new WeakReference(obj);
访问弱引用的目标对象:要访问弱引用指向的对象,需要使用Target
属性,并且在此之前,最好使用IsAlive
属性检查该对象是否还存在:
if (weakRef.IsAlive)
{
obj = weakRef.Target as object;
}
防止早期回收:当你创建了一个弱引用后,应避免保留原始的强引用,否则该对象不会被垃圾回收。
适当时机的使用:只有在需要大量内存并且这些内存可以在任何时间被释放的情况下才使用弱引用。如果一个对象需要长期保持活动状态,或者它占用的内存小,那么就不需要使用弱引用。
空值处理:由于弱引用的对象可能会在任何时间被删除,所以在访问前需进行空值检查,确保代码能够正确处理返回值为 null
的情况。
谨慎使用终结器:默认情况下,如果对象拥有终结器,弱引用将继续引用对象直到运行终结器。您可以创建“长”弱引用(通过在构造WeakReference
时传递true
)来更改此行为,但必须谨慎操作,以免出现意外的内存泄漏。
弱事件模式:在实现事件监听器时,考虑使用弱事件模式来避免事件源无法因订阅者已经不存在而被垃圾回收带来的内存泄漏。
请记住,尽管弱引用在某些情况下可能非常有用,但在大部分情况下你可能并不需要它们。只有在确实需要精细控制对象生命周期时,才建议使用弱引用。
这里使用一段不太合适的代码作为简单的演示,为什么这么说大家看看执行。
public class TestClass
{
~TestClass()
{
// 这是一个终结器
}
}
// 创建一个强引用对象
TestClass strongRef = new TestClass();
// 对强引用对象进行弱引用
WeakReference weakRef = new WeakReference(strongRef);
// 释放强引用
strongRef = null;
// 调用垃圾回收器
GC.Collect();
GC.WaitForPendingFinalizers(); // 等待终结器完成
// 检查弱引用的状态
Console.WriteLine(weakRef.IsAlive ? "Object has not been collected." : "Object has been collected.");
执行完代码之后会发现结果一直都是"Object has not been collected." 为什么?
可能是因为垃圾回收器并不总是立即运行,它的运行取决于许多因素,包括系统的内存压力、CLR的实现细节等。即使调用了GC.Collect()
方法,也不能100%保证垃圾回收器会立即回收所有的待处理对象。
另外,如果被弱引用的对象实现了终结器(Finalizer)或者析构函数,那么对象的清理过程会被延迟,因为CLR需要执行对象的终结器,然后在下一次垃圾回收时才真正清理该对象。这种情况下,即使强引用已经被置为null,并且调用了GC.Collect()
,weakRef.IsAlive
仍然可能返回true。
https://learn.microsoft.com/zh-cn/dotnet/standard/garbage-collection/weak-references