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

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

终止化

终止化操作允许一种资源在他所占的内存被回收之前首先执行一些清理工作。要提供终止化操作操作,必须为类型实现一个名为Finalize的方法。当垃圾收集器判定一个对象为可收集的垃圾时,它便会调用该对象的Finalize方法(如果存在的话)。

  C#为定义Finalize方法提供了特殊的语法看下面代码;

public class OSHandler
{
private IntPtr handler;

public OSHandler(IntPtr handler)
{
handler = handler;
}

// 当垃圾收集器执行时,该析构函数将被调用,它将关闭非托管资源句柄
~ OSHandler()
{
CloseHandler(handler);
}

public IntPtr ToHandler()
{
return handler;
}

// 释放非托管资源
[System.Runtime.InteropServices.DllImport( " Kernel32 " )]
private extern static bool CloseHandler(IntPtr handler);
}

查看中间语言

.method family hidebysig virtual instance void
Finalize() cil managed
{
// 代码大小 26 (0x1a)
.maxstack 1
. try
{
IL_0000: nop
IL_0001: ldarg. 0
IL_0002: ldfld native int FinalizeStudy.OSHandler:: ' handler '
IL_0007: call bool FinalizeStudy.OSHandler::CloseHandler(native int )
IL_000c: pop
IL_000d: nop
IL_000e: leave.s IL_0018
} // end .try
finally
{
IL_0010: ldarg. 0
IL_0011: call instance void [mscorlib]System.Object::Finalize()
IL_0016: nop
IL_0017: endfinally
} // end handler
IL_0018: nop
IL_0019: ret
} // end of method OSHandler::Finalize

 

会发现,析构函数被编译器编译为Finalize函数,并且使用了异常处理。

  这样当未来某个时刻垃圾收集器判定对象为可收集的垃圾时,它会看到该类型定义有一个Finalize方法,于是它便会调用该方法,从而允许CLoseHandler函数来关闭其中的非托管资源。在Finalize方法返回之后的某个时刻,该OSHandler对象在托管堆中所占的内存才会被回收。

  应该避免使用Finalize方法。有以下原因:

  • 实现了Finalize的对象其代龄会被提高,增加内存的压力,甚至被该对象直接或者间接引用的对象的代龄也将被提升(以后学习代龄)。
  • 终止化对象的分配花费的时间较长,因为指向它们的指针必须被放在终止化链表上;
  • 强制垃圾收集器执行Finalize方法会极大的损失程序的性能;
  • 不能控制Finalize方法何时执行。对象可能会一直占有着资源,直到出现垃圾收集;
  • CLR不对Finalize方法的执行顺序做任何的保障。加入对象包含指向另一个对象的指针,两个对象都可能会被垃圾收集,顺序的不一样会导致结果不可预期。靠,个人感觉这就是一个bug。

终止化操作的内部机理

  创建一个新对象,new先为对象在托管堆上面分配内存。如果对象的类型定义了FInalize方法,那么在该类型的实例被调用之前,指向该对象的一个指针将被放到一个称为终止化链表(finalization list)的数据结构里面。终止化链表是一个由垃圾收集器控制的内部数据结构。链表上的每一个条目都引用着一个对象。这实际告诉垃圾收集器在回收这些对象的内存之前要首先调用它们的Finalize方法。

  当垃圾收集检测到可收集的垃圾时,垃圾收集器会扫描终止化链表是否有执行可收集垃圾的对象,当找到这样的指针,它们会从终止化链表移除,并添加到一个称为终止化可达列表(freachable queue)的数据结构上。在终止化可达列表上出现的对象表示该对象的Finalize方法即将被调用,当垃圾收集完毕后,没有Finalize的对象的内存将被回收,实现了Finalize的对象内存却不能被回收,因为他们的Finalize方法还没有被调用。CLR有一个特殊的高优先级的线程用来专门调用Finalize方法。该线程可以避免线程同步问题。

  非常有意义的是,当垃圾收集器将一个对象从终止化链表转移到终止化可达队列时,该对象不再认为是可收集的垃圾对象,它的内存也就不可能被回收。到此为止,垃圾收集器完成了垃圾对象的鉴别工作,一些原先认为是垃圾的对象现在被认为不是垃圾,从某种意义上来说,对象又“复苏”了。当第一次垃圾收集执行完毕后,特殊的CLR线程将会清空终止化可达队列中的对象,同时执行其中某个对象的Finalize方法。

  等下一次垃圾收集执行的时候,它会看到这些终止化对象已经成为真正的垃圾对象,这样实现了Finalize的对象的内存才被完全回收。 实际上终止化对象需要执行两次垃圾收集才能释放它所占用的内存。实际上由于代龄的提高,可能收集次数会多于两次。上面这些玩意在Effective C#里面也讲过,以前没有看懂。

 

Dispose模式

  感觉CLR的终止化是个吃力不讨好的玩意

  • 分配起来慢(加入终止化链表),
  • 收集起来更慢,先是加入可达终止化列表,让对象复活,二次垃圾回收才能收集;
  • 不能人为的控制,长时间占用内存;
  • 增加对象的代龄,更是不可饶恕。
  • 怎么办??

  微软总是NB的,作者总是掉人胃口的,CLR提供了显式释放或者关闭对象的能力,但是类型需要实现一种被称为Dispose的模式(当然有一些约定)。如果一个类型实现了Dispose模式,使用该类型的开发人员将能够知道当对象不再被使用时如何显式地释放掉它所占用的资源。

   新版本的OSHandler实现,应用了Dispose接口:

public class OSHandler:IDisposable
{
private IntPtr handler;

public OSHandler(IntPtr handler)
{
this .handler = handler;
}

// 当垃圾收集器执行时,该析构函数将被调用,它将关闭非托管资源句柄
~ OSHandler()
{
Dispose( false );
}

public IntPtr ToHandler()
{
return handler;
}

// 释放非托管资源
[System.Runtime.InteropServices.DllImport( " Kernel32 " )]
private extern static bool CloseHandler(IntPtr handler);


public void Dispose()
{
// 因为对象的资源被显示清理,所以在这里阻止垃圾收集器调用Finalize方法
GC.SuppressFinalize( this );
// 进行实际清理工作
Dispose( true );

}

// 可以替换Dispose方法
public void Close()
{
Dispose();
}

// 执行清理工作,protected为了子类
protected void Dispose( bool disposing)
{
// 线程安全
lock ( this )
{
if (disposing)
{
// 对象正在被被显式关闭,此时可以引用其他对象,因为Finalize方法还没有被执行
}
}
if (IsValid)
{
// 如果handler有效,那么关闭之
CloseHandler(handler);

handler = InvalidHandler; // 置为无效,防止多次调用
}
}
// 返回一个无效的句柄值
public IntPtr InvalidHandler{ get { return IntPtr.Zero;}}

// 判断句柄是否有效
public bool IsValid { get { return handler != InvalidHandler; } }
}

  调用上面的Dispose或者Close方法只是显式释放非托管资源,并不会释放托管堆中占用的内存,释放对象内存的工作仍然由垃圾收集器负责,当然释放时间仍然是不确定的。 

  上面的代码中Finalize中Dispose方法的disposing参数被设为fasle。这将告诉Dispose方法不应该执行任何其他对象的代码。在Close和无参Dispose方法中disposing参数为true,因为是手动执行,程序逻辑可以控制,可以在if中执行代码。调用SuppressFinalize主要是为了避免终止化对象给垃圾收集器带来负担。

  既然已经有了手动关闭的方法,为什么还要实现Finalize方法呢,因为我们不能保证程序的使用者一定会调用Dispose方法或者Close方法,如果不调用将会造成资源浪费,甚至系统崩溃,但是这不是使用者的错误,我们的程序应该考虑到这一点,实现Finalize就是为了防止这种情况出现,作为一个后备吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/436792.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python time 时间戳_Python的time.time()返回本地或UTC时间戳吗?

这是可以在文本文件中使用的时间戳记的文本形式。(问题的标题在过去是不同的,因此对此答案的介绍进行了更改,以阐明如何将其解释为时间。[2016年1月14日更新])您可以使用.now()或.utcnow()来将时间戳记作为字符串获取datetime.datetime:>&…

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

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

mysql实验三单表和多表查询_数据库实验三(单表查询)

实验三:select sno,snamefrom student;//(1)查询全体学生的学号和姓名select *from student;//(2)查询全体学生的详细记录select sname,sage,sdeptfrom student where sdeptMA;//(3)查询软件学院的学生姓名、年龄、系别select distinct snofrom sc;//(4)查询所有选修过课程的学…

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

弱引用 当一个根指向一个对象时,该对象不可能被垃圾收集器收集,在这种情况下,通常说存在一个该对象的强引用(strong reference)。垃圾收集器还支持弱引用(weak reference)的概念。弱引用允许垃圾…

1756冗余_AB冗余模块1756-RM

AB冗余模块1756-RM100-C30UKJ01100-C30UKJ10100-C30UKL00100-C30UKL10AB冗余模块1756-RM100-C30UKP00100-C30UL00AB冗余模块1756-RM100-C30UL10100-C30UN00AB冗余模块1756-RM100-C30UN10100-C30UP001756-RM ControLogix冗余模块140U-H-RM12B 140U塑壳断路器外部附件1756-RMC1 C…

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

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

imp命令导入指定表_Oracle—— 导入/导出 数据:exp,imp 命令

exp,imp 命令是需要在windows 的 cmd 命令中执行的命令,主要用于数据的导入和导出工作,方便高效。远程地址—— ip:port/orcl 注:该远程地址不写,就意味着执行本地的库。Oracle的数据导出(1)导出数据的…

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

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

MySQL数据库开发理念_mysql之数据库基本理念

数据储存的问题:数据冗余和不一致数据访问困哪数据孤立完整性原子性问题例如AB2个账户,从A中向B转钱,不管如何转,A和B的总量是保持不变的。只要A减少,B就增加。并发访问异常安全性问题文件的分层:表示层&am…

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

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

少年自学python笔记_自学python 笔记

print() 输入input() 输出python能够处理的数据类型:整数、浮点数、字符串("\"转义符,\n 换行)、布尔值/布尔代数(True、False(and【或】、or【于】、not【非】))(and:只有所有都为True,and运算结果才为True)(or:只要一个为True,or…

【转】深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第四节 参数传递对堆栈的影响 1

前言 虽然在.Net Framework 中我们不必考虑内在管理和垃圾回收(GC),但是为了优化应用程序性能我们始终需要了解内存管理和垃圾回收(GC)。另外,了解内存管理可以帮助我们理解在每一个程序中定义的每一个变量是怎样工作的。 简介 这篇文章我们将介绍一些方…

java解析string_java读取文件内容为string字符串的方法

直接就把项目中的方法贴出来吧/*** 读出城市列表文件*/private String readCityFile() {File file02 new File(path_xinfu, "/cityList.json");FileInputStream is null;StringBuilder stringBuilder null;try {if (file02.length() ! 0) {/*** 文件有内容才去读文…

【转】深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第四节 参数传递对堆栈的影响 2

前言 虽然在.Net Framework 中我们不必考虑内在管理和垃圾回收(GC),但是为了优化应用程序性能我们始终需要了解内存管理和垃圾回收(GC)。另外,了解内存管理可以帮助我们理解在每一个程序中定义的每一个变量是怎样工作的。 简介 继续上篇未完成的“参数传…

java 引用被回收_java GC 静态List 如果没有引用会被回收吗

垃圾收算法1.引用计数法(Reference Counting Collector)2.tracing算法(Tracing Collector)3.compacting算法(Compacting Collector)4.copying算法(Coping Collector)5。generation算法(Generational Collector)6.adaptive算法(Adaptive Collector)一个新的对象被创建&#xff0…

【转】深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第五节 引用类型复制问题及用克隆接口ICloneable修复

前言 虽然在.Net Framework 中我们不必考虑内在管理和垃圾回收(GC),但是为了优化应用程序性能我们始终需要了解内存管理和垃圾回收(GC)。另外,了解内存管理可以帮助我们理解在每一个程序中定义的每一个变量是怎样工作的。 简介 这一节我们将介绍引用类型…

linux virt java_Linux下Java环境安装

本节主要讲解Linux(Centos 6.5)下Java环境的安装1. 卸载机器上默认安装的JDK在Linux环境下一般会默认安装jdk,为了自己项目的开发部署,一般情况要重新装jdk,而且自己装的Jdk相对来说易控制版本,稳定性更高。所以以下是我卸载预装J…

【转】深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第六节 理解垃圾回收GC,提搞程序性能****

前言 虽然在.Net Framework 中我们不必考虑内在管理和垃圾回收(GC),但是为了优化应用程序性能我们始终需要了解内存管理和垃圾回收(GC)。另外,了解内存管理可以帮助我们理解在每一个程序中定义的每一个变量是怎样工作的。 简介 这一节我们将介绍垃圾回…

java下拉列表 动态_【示例】教你简单用Java写一个动态更新的下拉列表(无数据库)...

动态更新下拉列表varxmlHttp;functioncreatXMLHttpRequest(){if(window.ActiveXObject){xmlHttpnewActiveXObject("Microsoft.XMLHTTP");}else if(window.XMLHttpRequest){xmlHttpnewXMLHttpRequest();}}functionupdateSelect(){varselecteddocument.all.slt1.value;…

【转】分布式事务的常见解决方案

一、事务起步 1. 什么是事务 事务这种东西大家都耳熟能详了,通常指由一组操作组成的一个工作单元,这一整个组合要么全部成功,要么全部失败。 2. 本地事务 在计算机系统中,更多的是通过关系型数据库来控制事务,这是…