弱引用是什么?

要搞清楚什么是弱引用,我们需要先知道强引用是什么。强引用并不是什么深奥的概念,其实我们平时所使用的.Net引用就是强引用。例如:

Cat kitty = new Cat();

变量kitty就是一个强引用,它指向了堆中的一个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中的弱引用的更多相关文章

  1. 理解Java中的弱引用(Weak Reference)

    本篇文章尝试从What.Why.How这三个角度来探索Java中的弱引用,理解Java中弱引用的定义.基本使用场景和使用方法.由于个人水平有限,叙述中难免存在不准确或是不清晰的地方,希望大家可以指出, ...

  2. C# 中的弱引用 WeakReference

    C#中的弱引用(WeakReference)   我们平常用的都是对象的强引用,如果有强引用存在,GC是不会回收对象的.我们能不能同时保持对对象的引用,而又可以让GC需要的时候回收这个对象呢?.NET ...

  3. WeakHashMap<K,V> 中的弱引用

    相信很多人对WeakHashMap并没有完全理解. WeakHashMap 持有的弱引用的 Key. 1. 弱引用的概念: 弱引用是用来描述非必需对象的,被弱引用关联的对象只能生存到下一次垃圾收集发生 ...

  4. .NET中的弱引用

    弱引用是什么? 要搞清楚什么是弱引用,我们需要先知道强引用是什么.强引用并不是什么深奥的概念,其实我们平时所使用的.Net引用就是强引用.例如: Cat cat = new Cat(); 变量cat就 ...

  5. C#中的弱引用(WeakReference)

    我们平常用的都是对象的强引用,如果有强引用存在,GC是不会回收对象的.我们能不能同时保持对对象的引用,而又可以让GC需要的时候回收这个对象呢?.NET中提供了WeakReference来实现.弱引用可 ...

  6. 关于C#中的弱引用

    本文前部分来自:http://www.cnblogs.com/mokey/archive/2011/11/24/2261605.html 分割线后为作者补充部分. 一:什么是弱引用 了解弱引用之前,先 ...

  7. Android开发过程中使用弱引用解决内存泄露的习惯

    Java虽然有垃圾回收,但是仍然存在内存泄露,比如静态变量.缓存或其他长生命周期的对象引用了其他对象,这些被引用的对象就会长期不能被GC释放,导致内存泄露. 弱引用(WeakReference)是解决 ...

  8. Java中的弱引用

    Strong references StringBuffer buffer = new StringBuffer(); 普通的对象创建都是这种类型,只要buffer还存在,对象就不会被GC回收.同时也 ...

  9. 简单聊一聊JS中的循环引用及问题

    本文主要从 JS 中为什么会出现循环引用,垃圾回收策略中引用计数为什么有很大的问题,以及循环引用时的对象在使用 JSON.stringify 时为什么会报错,怎样解决这个问题简单谈谈自己的一些理解. ...

随机推荐

  1. PEAR安装

    看到PEAR章节,提到安装PEAR需要go-pear.bat,我机器上的PHP(v7.0.8)目录下,并没有go-pear.bat这个文件,网上查了一遍,怎么说的都有,最后还是在官网上找到解决方案. ...

  2. Android SDK Manager无法更新的解决[ 转]

    将下列内容行添加到hosts文件中: 74.125.237.1 dl-ssl.google.com 1.Windows C:\WINDOWS\system32\drivers\etc\Hosts 2. ...

  3. Python: Win7 64位如何安装MongoDB?

    Windows 7 64位安装MongoDB 官网tutorial:  https://docs.mongodb.com/v3.0/tutorial/install-mongodb-on-window ...

  4. dubbo升级spring4与cxf

    阿里的dubbo项目是基于spring2.x的.但之后停止更新了.当当网有根据dubbo扩展成dubbox,基于spring3.x,但我的项目中用的是spring4.x,于是fork了dubbox,自 ...

  5. ecshop 不同页面调用不同分类文章的解决办法

    调用文章列表,需要修改对应的程序,修改index.php或者arctical_cat.php文件在$smarty->assign('new_articles', index_get_new_ar ...

  6. String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";作用!!!!!

    <%String path = request.getContextPath();String basePath = request.getScheme()+"://"+re ...

  7. tp5 model 中的查询范围(scope)

    查询范围scope在model中定义,在controller中使用 namespace app\index\model; use think\Model; class User extends Mod ...

  8. Linux-/proc目录简介

    Linux /proc目录简介 1.简单了解 以文件系统的方式为访问系统内核数据的操作提供接口 由linux内核提供:通过/proc文件系统,在运行时访问内核内部数据结构.改变内核设置的一种机制 pr ...

  9. Destroying The Graph(poj 2125)

    题意: 给你一张有向图,你可以选择一个点:• 摧毁其所有的入边,代价A[i].• 摧毁其所有的出边,代价B[i].• 求摧毁这张图的最小代价.• 数据范围1000 /* 很经典的一道题目(我这么弱,稍 ...

  10. MongoDB安装并随windows开机自启

    MongoDB的官方下载站是http://www.mongodb.org/downloads,可以去上面下载最新的程序下来.在下载页面可以看到,对操作系统支持很全面,OS X.Linux.Window ...