.NET中的弱引用
弱引用是什么?
要搞清楚什么是弱引用,我们需要先知道强引用是什么。强引用并不是什么深奥的概念,其实我们平时所使用的.Net引用就是强引用。例如:
Cat cat = new Cat();
变量cat就是一个强引用,它指向了堆中的一个Cat对象实例。我们都知道,CLR的垃圾回收机制会标记所有被强引用到的对象,而那些剩下的未被标记的对象则会被垃圾回收。换句话说,如果一个对象一直被某个强引用所指向,那么它是不会被垃圾回收的。
从这一点来看,弱引用就完全不一样了——即使某个对象被弱引用所指向,该对象仍然会被垃圾回收。也就是说,弱引用不会影响对象的生命周期。
System.WeakReference类是.net为我们提供的一个弱引用的实现,可以这么用:
WeakReference weakReference = new WeakReference(new Cat());
Cat strongReference = weakReference.Target as Cat;
if (strongReference != null)
{
// Cat对象实例尚未被垃圾回收,可以通过strongReference进行访问
}
else
{
// Cat对象实例已被垃圾回收
}
如果在上例的第一行代码之后第二行代码之前,CLR发生了一次垃圾回收,那么可以基本断定那个Cat对象实例已经不存在了,此时weakReference.Target是null。
WeakReference类型还有一个构造函数的重载为:
Public WeakReference(Object target, bool trackResurrection)
其中bool类型的参数trackResurrection指定了这个WeakReference实例是一个长弱引用还是一个短弱引用。对于短弱引用,当它所指向的对象被垃圾回收机制标记为“不可达”状态(即将被回收)时,该弱引用的Target属性即为null。而对于长弱引用,当它所指向对象的析构函数被调用之后,它的Target属性仍然是有效的。
弱引用的内部实现
弱引用看起来很神奇,似乎是凌驾于正常的垃圾回收机制之上的,它究竟是如何实现的呢?其实WeakReference类型在内部封装了一个名为GCHandle的struct类型,正是这个GCHandle使弱引用成为可能。
CLR中的每个AppDomain都拥有一个GC句柄表。这个表的每一项记录有两个信息,一个是指向堆中某个对象的指针,另一个是这个表项的类型。总共有4种表项类型,其中Weak和WeakTrackResurrection两种类型和我们今天所讨论的弱引用相关。GCHandle这个类提供了一些操纵GC句柄表的方法。我们可以使用它的Alloc方法向GC句柄表中添加一个指定类型的表项。当垃圾回收开始后,垃圾回收器找到所有可达对象(简单的说,就是有用的对象)。然后遍历GC句柄表中每个Weak类型的表项,如果发现某表项所指的对象不属于可达对象,则会把该表项的对象指针设置为null。紧接着,垃圾回收器会找出所有不可达对象中定义了析构函数的对象,并把他们放到一个被称为freachable的队列中(freachable中的对象会等待一个CLR中特定的线程来调用他们的终结函数)。由于这些freachable中的对象现在又被freachable队列所引用,所以它们又成为可达对象了。此时,垃圾回收器会遍历GC句柄表中所有WeakTrackResurrection类型的表项,和刚才一样,如果某表项所指的对象不属于可达对象,则会把该表项的对象指针设置为null。此处需注意,对于那些一开始被判定为不可达且定义了析构函数的对象来说,它们在GC句柄表中所对于的表项指针仍然不是null。这就是Weak和WeakTrackResurrection两种类型的区别。
WeakReference就是通过表示了某个GC句柄表表项的GCHandle对象来完成跟踪对象生命周期的功能的。你也一定可以看出短弱引用利用了Weak类型的GC句柄表项,而长弱引用则利用了WeakTrackResurrection类型的表项。
WeakReference的一些注意事项
首先,WeakReference自身也实现了析构函数。也就是说,它即使不再被使用了,也不会被立即回收,而是会在内存里赖着多活一会(可能会经历不止一次的垃圾回收)。
另外,如上一节所说,WeakReference会向GC句柄表添加一个表项。而每次垃圾回收,GC句柄表都会被遍历一遍。可想而知,如果系统中存在大量的WeakReference,那么GC句柄表很可能也会非常庞大,导致垃圾回收的效率降低。
WeakReference经常会和缓存联系起来,但是它并不适和用来实现一个大型的缓存机制。这是为什么呢?一方面如前文所述,WeakReference自身实现了析构函数,也有可能导致垃圾回收的效率降低,因此应该避免在内存中创建大量的WeakReference对象实例。另一方面,我们知道一个对象如果没有被任何强引用所指向,而仅仅被弱引用所指向,那么它很有可能活不过一次垃圾回收。所以通过这样的方式所实现出来的缓存机制势必有着非常短促的缓存策略,而这种策略在大部分情况下都不会是你期望得到的。
WeakReference的三个使用场景
对象缓存
试想这样一个场景,我有一个内存受限的程序,在这个程序里经常会使用一个占用很多内存的位图对象,所幸生成这个位图对象并不复杂。所以我每次要使用那个位图对象的时候都会重新生成它,使用完毕之后就将其丢弃(不保留它的引用)。
这种方式完全能够满足我的需求,但是还能不能再优化呢?分析一下我们就可以发现,当我需要使用位图对象的时候,我上次使用的那个位图对象虽然被我丢弃了,但可能仍然没有被垃圾回收,仍然存在内存中。此时如果我能直接使用这个位图对象,就可以节省出因重建位图对象而浪费的内存和CPU资源。
改进措施很简单——使用完位图对象后,不是直接丢弃,而是用一个弱引用指向它。待下次访问位图对象时,就可以先通过弱引用判断位图对象是否还在内存中,如果还在则直接使用,否则重新创建。
辅助调试
有时,对于某种类型,我们需要知道当前程序中存在有多少对象实例,以及存在的都是哪些实例,以便于我们进行一些性能分析。这时,我们就可以使用到弱引用了。例如下面的代码:
public class A
{
private static List<WeakReference> _instances = new List<WeakReference>(); public A()
{
_instances.Add(new WeakReference(this));
} public static int GetInstanceCount()
{
GC.Collect();
return _instances.Count(x => x.Target != null);
}
}
GetInstanceCount方法可以得到内存中A类型的实例个数。另外,还可以通过instances集合来检查内存中的A类型实例都有哪些。在调试内存泄露问题的时候,这些信息都可以派上用场。
相比于各种性能分析Profiler工具,这种方法更加轻巧便捷。
弱事件
.Net中的事件有时会引起内存泄露问题。例如,A注册了B的某个事件,此时B就会暗中保留A的一个强引用,导致A无法被内存回收,直到B被回收或A反注册了B的事件。例如,我有一个对象注册了主窗口的Loaded事件,只要我不反注册该事件,那么主窗口会一直引用该对象,直到主窗口被关闭,该对象才会被回收。所以,每当我们注册某个对象的事件时,都有可能在不经意间埋下内存泄露的隐患。
解决这个问题的根本方法是,在必要的时候进行事件的反注册。但是,在某些情况下,我们可能很难判定这个“必要的时候”。另外,当我们作为类库的提供者时,我们也很难保证类库的使用者都记得要反注册事件。因此,另一个解决方案就是使用弱事件。
弱事件的实现原理很简单,就是对事件进行一层封装。不让事件发布者直接引用监听者,而是让他们保留一个监听者的弱引用。当事件触发时,发布者会先检查监听者是否还存在于内存中,如果存在才通知它。如此一来,监听者的生命周期就不会依赖于发布者了。
.NET中的弱引用的更多相关文章
- 理解Java中的弱引用(Weak Reference)
本篇文章尝试从What.Why.How这三个角度来探索Java中的弱引用,理解Java中弱引用的定义.基本使用场景和使用方法.由于个人水平有限,叙述中难免存在不准确或是不清晰的地方,希望大家可以指出, ...
- C# 中的弱引用 WeakReference
C#中的弱引用(WeakReference) 我们平常用的都是对象的强引用,如果有强引用存在,GC是不会回收对象的.我们能不能同时保持对对象的引用,而又可以让GC需要的时候回收这个对象呢?.NET ...
- WeakHashMap<K,V> 中的弱引用
相信很多人对WeakHashMap并没有完全理解. WeakHashMap 持有的弱引用的 Key. 1. 弱引用的概念: 弱引用是用来描述非必需对象的,被弱引用关联的对象只能生存到下一次垃圾收集发生 ...
- 简单说说.Net中的弱引用
弱引用是什么? 要搞清楚什么是弱引用,我们需要先知道强引用是什么.强引用并不是什么深奥的概念,其实我们平时所使用的.Net引用就是强引用.例如: Cat kitty = new Cat(); 变量ki ...
- 关于C#中的弱引用
本文前部分来自:http://www.cnblogs.com/mokey/archive/2011/11/24/2261605.html 分割线后为作者补充部分. 一:什么是弱引用 了解弱引用之前,先 ...
- Android开发过程中使用弱引用解决内存泄露的习惯
Java虽然有垃圾回收,但是仍然存在内存泄露,比如静态变量.缓存或其他长生命周期的对象引用了其他对象,这些被引用的对象就会长期不能被GC释放,导致内存泄露. 弱引用(WeakReference)是解决 ...
- C#中的弱引用(WeakReference)
我们平常用的都是对象的强引用,如果有强引用存在,GC是不会回收对象的.我们能不能同时保持对对象的引用,而又可以让GC需要的时候回收这个对象呢?.NET中提供了WeakReference来实现.弱引用可 ...
- Java中的弱引用
Strong references StringBuffer buffer = new StringBuffer(); 普通的对象创建都是这种类型,只要buffer还存在,对象就不会被GC回收.同时也 ...
- 九、Android学习笔记_ Android开发中使用软引用和弱引用防止内存溢出
在<Effective Java 2nd Edition>中,第6条“消除过期的对象引用”提到,虽然Java有 垃圾回收机制,但是只要是自己管理的内存,就应该警惕内存泄露的问题,例如的对象 ...
随机推荐
- 加载程序到android虚拟机报错: android.widget.RelativeLayout cannot be cast to android.widget.Button
05-23 02:53:48.416: E/Trace(875): error opening trace file: No such file or directory (2) 05-23 02:5 ...
- POJ 3253 Fence Repair (贪心)
Fence Repair Time Limit:2000MS Memory Limit:65536KB 64bit IO Format:%I64d & %I64u Submit ...
- LeetCode 283
Move Zeros Given an array nums, write a function to move all 0's to the end of it while maintaining ...
- [改善Java代码]不要随便设置随机种子
建议30: 不要随便设置随机种子 随机数在太多的地方使用了,比如加密.混淆数据等,我们使用随机数是期望获得一个唯一的.不可仿造的数字,以避免产生相同的业务数据造成混乱.在Java项目中通常是通过Mat ...
- [设计模式]<<设计模式之禅>>模板方法模式
1 辉煌工程——制造悍马 周三,9:00,我刚刚坐到位置上,打开电脑准备开始干活. “小三,小三,叫一下其他同事,到会议室开会”,老大跑过来吼,带着坏笑.还没等大家坐稳,老大就开讲了:“告诉大家一个好 ...
- 关于修改Eclipse工作空间对应的文件夹名称之后的处理.
把文件夹名字从"xhkong"变成"xhkong(maintenance5.6)"之后打开这个工作空间. 导入git仓库我发现了一个之前没有发现的小技巧. 导入 ...
- javascript工具--控制台详解(转自 阮一峰博客)
大神这篇博客是写在2011年,主要介绍 “Firefox” 浏览器插件 “Firebug” 的操作,如今主流浏览器对控制台都已经提供了很好的支持.我自己用的最多是谷歌的 “chrome” 浏览器,下面 ...
- 关于java.lang.NullPointerException: Module 'null' not found.的问题
在用eclipse做struts1项目时,配置都ok了.可是一运行就报java.lang.NullPointerException: Module 'null' not found.错. 代码如下: ...
- 也谈Asp.net 中的身份验证
钱李峰 的这篇博文<Asp.net中的认证与授权>已对Asp.net 中的身份验证进行了不错实践.而我这篇博文,是从初学者的角度补充了一些基础的概念,以便能有个清晰的认识. 一.配置安全身 ...
- Cocos2d-x实例:设置背景音乐与音效- AppDelegate实现
为了进一步了解背景音乐和音效播放的,我们通过一个实例给大家介绍一下.如下图所示有两个场景:HelloWorld和Setting.在HelloWorld场景点击“游戏设置”菜单可以切换到Setting场 ...