弱引用

  当一个根指向一个对象时,该对象不可能被垃圾收集器收集,在这种情况下,通常说存在一个该对象的强引用(strong reference)。垃圾收集器还支持弱引用(weak reference)的概念。弱引用允许垃圾收集器收集对象,同时也允许应用程序访问该对象,结果取决于时间。

  如果对象的弱引用存在,那么在垃圾收集执行后,该对象的内存将被执行清理。另一方面,要访问一个弱引用对象,应用程序必须获取该对象的一个强引用。如果应用程序在对象被执行之前得到了它的强引用,那么垃圾收集器将不再对该对象执行垃圾收集,因为这时存在一个该对象的强引用。

  下面代码展示了弱引用的使用

class Program
{
static void Main( string [] args)
{
SomeWeakReferenceMethod();
SomeStrongtReferenceMethod();
}

static void SomeWeakReferenceMethod()
{
// 创建一个强引用对象
SomeClass o = new SomeClass();

WeakReference wr = new WeakReference(o);

o = null ; // 移除对象的强引用

o = wr.Target as SomeClass;

if (o == null )
{
// 出现过垃圾回收,对象的内存已经被回收
Console.WriteLine( " 已经被回收 " );
}
else
{
// 未出现垃圾回收
Console.WriteLine( " 没有被回收 " + o.X.ToString());
}

}
static void SomeStrongtReferenceMethod()
{
// 创建一个强引用对象
SomeClass o = new SomeClass();
o = null ; // 移除对象的强引用
try
{
Console.WriteLine(o.X);
}
catch
{
Console.WriteLine( " error " );
}
}

}

public class SomeClass
{
private int x;

public int X
{
get
{
return this .x;
}
}

public SomeClass()
{
x = 5 ;
}
}

个人感觉弱引用机制有点像缓存,将一些操作起来比较费时(比如便利系统的硬盘)的东西第一次获取之后暂时放入缓存中,并且将其引用置为null来减少应用程序的压力,但是放置在缓存中可以再次使用(减少读取时的压力),至于是否能够再次使用成功要看垃圾收集器。如果执行过垃圾收集,弱引用的对象将会被回收掉当然就无法再次使用了。

  System.WeakReference有两个公用构造器:

  • public WeakReference(object target);
  • public WeakReference(object target, bool trackResurrection);

  target表示要追踪的对象(上例中为SomeClass), trackResurrection表示是否要追踪对象的复苏,换句话说就是对象在执行Finalize方法后,是否还要追踪该对象(对象执行Finlize后对象复苏了) 。

  将不追踪对象复苏的WeakReference称为短弱引用(short weak reference),而将追踪对象复苏的称为长弱引用(long weak reference)。如果一个对象没有Finalize方法,长短弱引用是一样的,最好不要使用长弱引用,因为长弱引用在一个对象被执行终止后允许该对象复苏,将会导致对象的状态不可预知。

  一旦创建了对象的弱引用,通常将该对象的强引用设置为null,为了再次使用该对象,需要将弱引用转换为一个强引用,通过WeakReference的Target属性来完成的。如果target为null,那么对象已经被执行了垃圾收集,否则将会得到该对象的强引用。这时对象将不会被垃圾收集器收集了。

弱引用的内部机理

  需要再次探究托管堆。托管堆中包含了两个内部数据结构来管理弱引用。即短弱引用表和长弱引用表。这两个表包含着一些指针,他们引用着托管堆对象。

  当创建一个WeakReference对象时,它会在两个弱引用表中选择一个(长短弱引用相应对应),并在其中寻找一个空白插槽。该插槽的值将被设为我们希望追踪对象的地址---也就是new WeakReference构造的那个对象的地址,弱引用表将不会认为是应用程序的根,否则垃圾收集器将不能收集它们中的指针引用的对象。

  垃圾收集器运行时发生的一系列事情:

  • 垃圾收集器构造一个可达对象的图(.net框架读书笔记---CLR内存管理\垃圾收集(一));
  • 垃圾收集器扫描短弱引用表,如果表中的对象不是前面可达对象图的一部分(对象已经不是根,在上面的例子中o已经被置为null了),那么表示该对象是一个不可达的对象,将短弱引用表中对应插槽的值将被设置为null(Target为null了);
  • 垃圾收集器扫描终止化链表(.net框架读书笔记---CLR内存管理\垃圾收集(二))。如果该链表中指针引用的对象不是可达对象图的一部分,那么该对象将是不可达对象,它将被从终止化链表转移到终止化可达队列上。这时对象由成为可达对象图的一部分了。
  • 垃圾收集器扫描长弱引用表,如果表中的对象不是前面可达对象图的一部分(该图现在已经包括终止化可达队列中引用的对象了),那么表示该对象是一个不可达的对象,将长弱引用表中对应插槽的值将被设置为null(Target为null了);
  • 垃圾收集器压缩内存,填充不可达对象空出的位置。

  继续分析代码

if (o == null )
{
// 出现过垃圾回收,对象的内存已经被回收
Console.WriteLine( " 已经被回收 " );
}
else
{
// 未出现垃圾回收
Console.WriteLine( " 没有被回收 " + o.X.ToString());
}

因为在此之前o已经被置为null了,所以它已经是一个不可达对象,如果执行垃圾收集器,o自然不在可达对象图中,那么垃圾收集器将会将短弱引用表中插槽对应的值设为null,这样Target将返回null,上面代码自然会执行if里面的代码。如果垃圾收集器还没有执行,虽然o已经为null,但是短弱引用插槽依然保存着对象的引用,那么Target将会返回对象的引用,使对象继续可达,可以使用,当然上面代码就会执行else里面的代码。

  短弱引用并不追踪对象的复苏。只要垃圾收集器判断对象成为不可达的对象,它就会把短弱引用表中对应的指针设置为null。如果对象重写了Finalize方法,那么这时该方法还没有被调用,所以对象仍然存在。此时target仍然返回null,虽然这时对象已经进入终止化可达队列,对象仍然存在。

【转】.net框架读书笔记---CLR内存管理\垃圾收集(四)相关推荐

  1. 【转】.net框架读书笔记---CLR内存管理\垃圾收集(三)

    接上一篇.net框架读书笔记---CLR内存管理\垃圾收集(二),主要学习了终止化对象(实现了Finalize方法的对象),了解了终止化对象的弊端,学习了通过实现IDisposable接口,通过Dis ...

  2. 【转】.net框架读书笔记---CLR内存管理\垃圾收集(一)

    一.垃圾收集平台基本原理解析 在C#中程序访问一个资源需要以下步骤: 调用中间语言(IL)中的newobj指令,为表示某个特定资源的类型实例分配一定的内存空间. 初始化上一步所得的内存,设置资源的初始 ...

  3. 【转】.net框架读书笔记---CLR内存管理\垃圾收集(六)

    对象代龄 代龄是旨在提高垃圾收集器性能的一种机制.有以下几点: 对象越新,其生存期越短: 对象越老,其生存期越长: 对托管堆的一部分执行垃圾收集要比对整个托管堆执行垃圾收集速度要快. 在托管堆初始化时 ...

  4. 【转】.net框架读书笔记---CLR内存管理\垃圾收集(五)

    对象复苏     当一个终止化对象被认为死亡时,垃圾收集器可以强制使该对象获得重生(进入终止化可达队列),因为这样才能调用对象的Finalize方法.在Finalize方法被调用之后,它才算真正的死亡 ...

  5. 【转】.net框架读书笔记---CLR内存管理\垃圾收集(二)

    前几天学习了CLR垃圾收集原理和基本算法,但是那些是仅仅相对于托管堆而言的,任何非托管资源的类型,例如文件.网络资源等,都必须支持一种称为终止化(finalization)的操作. 终止化 终止化操作 ...

  6. .net框架读书笔记---CLR内存管理\垃圾收集(二)

    前几天学习了CLR垃圾收集原理和基本算法,但是那些是仅仅相对于托管堆而言的,任何非托管资源的类型,例如文件.网络资源等,都必须支持一种称为终止化(finalization)的操作. 终止化 终止化操作 ...

  7. 【转】.net框架读书笔记---CLR内存管理\垃圾收集(七)

    编程控制垃圾收集器 System.GC类型为应用程序提供了直接控制垃圾收集器的一些方法,可以通过GC.MaxGeneration来查询托管堆支持的最大代龄,目前为2. 通过下面方法执行垃圾收集器 GC ...

  8. 《代码的未来》读书笔记:内存管理与GC那点事儿

    一.内存是有限的 近年来,我们的电脑内存都有好几个GB,也许你的电脑是4G,他的电脑是8G,公司服务器内存是32G或者64G.但是,无论内存容量有多大,总归不是无限的.实际上,随着内存容量的增加,软件 ...

  9. 《Linux内核设计与实现》读书笔记(12)--- 内存管理(2)

    6.slab层 为了便于数据的频繁分配和回收,Linux内核提供了slab层(也就是所谓的slab分配器).slab分配器扮演了通用数据结构缓存层的角色. slab层把不同的对象划分为所谓高速缓存(c ...

最新文章

  1. 目标检测旋转增强源码
  2. 计算机视觉的发展历史
  3. linux 粘贴内容命令行,Linux下命令行中的复制和粘贴
  4. delphi5开发人员指南_非设计人员的网页设计开发人员指南
  5. python 抽象语法树_用python演示一个简单的AST(抽象语法树)
  6. 在elementUI中使用 el-autocomplete 实现远程搜索的下拉框
  7. Linux故障解决(4)——新安装的CentOS 系统无法上网解决方法 (未知的名称或服务)
  8. 关于计算机博弈的开源项目
  9. 一周信创舆情观察(12.13~12.19)
  10. 《人类简史》二、认知革命——上帝之手的秘密
  11. 硬编码支持情况(一)
  12. 三种嵌入式操作系统比较和分析
  13. python特征相关性热力图怎么画_百度热力图,带您探索城市的奥妙!
  14. 左偏树(XJT Love Trees,玲珑杯 Round#8 C lonlife 1081)
  15. 心理正常与异常的区分_正常心理与异常心理的判别标准
  16. Android Studio新建工程及测试效果
  17. admit commit permit
  18. 计算机网络技术三级英文,计算机三级网络技术真题整理(国外英文资料).doc...
  19. 基于RT-Thread系统的机智云数字仪表教程(一)——移植RT-Thread的BSP模板
  20. 腾讯云数据库TDSQL——极速体验tdsql私有云

热门文章

  1. SQL count和case when配合统计给定条件下不重复的记录数
  2. HDU 1284 钱币兑换问题 (动态规划 背包方案数)
  3. TcpClient.Connect函数连接超时的问题(转载)
  4. centos7 源码安装goaccess
  5. H265编码等级以及图像的基础知识
  6. 对‘example_app_new’未定义的引用
  7. 洛谷P3628 [APIO2010]特别行动队(斜率优化)
  8. 什么是大数据,怎么理解和应对大数据时代
  9. ▲数据结构 笛卡尔树【2011】五2 C++版
  10. HDU 3306 Another kind of Fibonacci