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

一、垃圾收集平台基本原理解析

  在C#中程序访问一个资源需要以下步骤:

  • 调用中间语言(IL)中的newobj指令,为表示某个特定资源的类型实例分配一定的内存空间。
  • 初始化上一步所得的内存,设置资源的初始状态,从而使其可以为程序所用。一个类型的实例构造器负责这样的初始化工作。
  • 通过访问类型成员来使用资源。
  • 销毁资源状态,执行清理工作。
  • 释放托管堆上面的内存,该步骤由垃圾收集器全权负责,值类型实例所占的内存位于当前运行线程的堆栈上,垃圾收集器并不负责这些资源的回收,当值类型实例变量所在的方法执行结束时,他们的内存将随着堆栈空间的消亡而自动消亡,无所谓回收。对于一些表示非托管的类型,在其对象被销毁时,就必须执行一些清理代码。

  当应用程序完成初始化后,CLR将保留(reserve)一块连续的地址空间,这段空间最初并不对应任何的物理内存(backing storage)(该地址是一段虚拟地址空间,所以要真正使用它,它必须为其“提交”物理内存),该地址空间即为托管堆。托管堆上维护着一个指针,暂且称之为NextObjPtr。该指针标识着下一个新建对象分配时在托管堆中所处的位置。刚开始的时候,NextObjPtr被设为CLR保留地址空间的基地址。

  中间语言指令newObj负责创建新的对象。在代码运行时,newobj指令将导致CLR执行以下操作:

  • 计算类型的所有实例字段(以及其基类型所有的字段)所需要的字节总数。
  • 在前面所的字节总数的基础上面再加上对象额为的附加成员所需的字节数:一个方法指针和一个SyncBlockIndex。
  • CLR检查保留区域中的空间是否满足分配新对象所需的字节数-----如果需要则提交物理内存。如果满足,对象将被分配在NextObjPtr指针所指的地方。接着类型的实例构造器被调用(NextObjPtr会被传递给this参数),IL指令newobj(或者说new操作符)返回其分配内存地址。就在newobj指令返回新对象的地址之前,NextObjPtr指针会越过新对象所处的内存区域,并指示出下一个新建对象在托管堆中的地址。

下图演示了包含A,B,C三个对象的托管堆,如果再分配对象将会被放在NextObjPtr指针所演示的位置(紧跟C之后)

2010050916425858.jpg

  在C语言中堆分配内存时,首先需要遍历一个链表数据结构,一旦找到一个足够大的内存块,该内存块就会被拆开来,同时链表相应节点上的指针会得到适当的调整。但是对于托管堆来说,分配内存仅仅意味着在指针上增加一个数值---显然要比操作链表的做法快许多,C语言都是在找到自由空间为其对象分配内存,因此连续创建几个对象,他们将很有可能被分散在地址空间的各个角落。但是在托管堆中,连续分配的对象可以保证它们在内存中也是连续的。

  就目前来看托管堆在实现的简单性和速度方面要远优于C语言的运行时中的堆。之所以这样是因为CLR做了大胆的假设---那就是应用程序的地址空间和存储空间是无限的,显然这是不可能的。托管堆必须应用某种机制来允许这种假设。这种机制就是垃圾回收器。

  当应用程序调用new创建对象时,托管堆可能没有足够的地址空间来分配该对象。托管堆通过将对象所需要的字节总数添加到NextObjPtr指针表示的地址上来检测这种情况。如果得到的结果超出了托管堆的地址空间范围,那么托管堆将被认为已满,这时就需要垃圾收集器。,其实这种描述是过于简单的,垃圾回收与对象的代龄有着密切的关系,还需继续学习垃圾收集。

二、垃圾收集算法

  垃圾收集器通过检查托管堆中是否有应用程序不再使用的对象来回收内存。如果有这样的对象,它们的内存将被回收。那么垃圾收集器是这样知道应用程序是否正在使用一个对象呢??还得继续学习。

  每一个应用程序都有一组根(root),一个根是一个存储位置,其中包含着一个指向引用类型的内存指针。该指针或者指向一个托管堆中的对象,或者被设置为null。例如所有的全局引用类型变量或静态引用类型都被认为是根。另外,一个线程堆栈上所有引用类型的本地变量或者参数变量也被认为是一个根。最后,在一个方法内,指向引用类型对象的CPU寄存器也被认为是一个根。

  当垃圾收集器开始执行时,它首先假设托管堆中的所有对象都是可收集的垃圾。换句话,垃圾收集器假设应用程序中没有一个根引用着托管堆中的对象。然后垃圾收集器便利所有的根,构造出一个包含所有可达对象的图。例如,垃圾收集器可能会定位出一个引用托管对象的全局变量。下图展示了分配有几个对象的托管堆,其中对象A,C,D,F为应用程序的根所直接引用。所有这些对象都是可达对象图的一部分。当对象D被添加到该图中时,垃圾收集器注意到它还引用着对象H,于是对象H被添加到该图,垃圾回收器就这样子以递归的方式来遍历应用程序中所有的可达对象。

2010050917454647.jpg

  一旦该部分的可达对象完成后,垃圾回收器将检查下一个根,并遍历其引用的对象。当垃圾回收器在对象之间进行遍历时,如果发现某对象已经添加到可达对象图中时(比如上图中的H,在检查D的时候已经将其添加到了可达对象图),它会停止沿着该对象标识的路径方向上遍历的活动。两个目的:

  • 可以避免垃圾收集器对一些对象的多次遍历,可高性能。
  • 如果两个对象之间出现了循环引用,可以避免遍历陷入无限循环(比如上图中D引用着H,而H又引用着D)。

  垃圾收集器一旦检查完所有的根,其得到的可达对象将包含所有从应用程序的根可以访问的对象。任何不在该图中的对象将是应用程序不可访问的对象,不可达的对象,因此也是可以被执行垃圾收集器的对象。垃圾收集器接着线性地遍历托管堆以寻找包含可收集垃圾对象的连续区域。

  PS:CLR的垃圾收集机制对我来说有点非主流,在此之前,我一直认为是垃圾收集器直接去寻找不可达的对象,现在看来垃圾收集器使用了逆向思维,通过找到可达对象来找到不可达的对象(这个原因还得继续思考)。

  如果找到了较大的连续区域,垃圾收集器将会把内存中的一些非垃圾对象搬移到这些连续区块中以压缩堆栈,显然搬移内存中的对象将使所有这些指向对象的指针变的无效。所以垃圾收集器必须修改应用程序的根以使它们指向这些对象更新后的位置。另外,如果任何对象包含有指向这些对象的指针,那么垃圾收集器也会负责矫正它们。托管堆被压缩以后,NextObjPtr指针将被设为指向最后一个非垃圾对象之后。下图展示了对于上面图执行垃圾收集器后的托管堆。

2010050918093015.jpg

  可见垃圾回收器对于应用程序的性能有不小的影响,CLR采用了代龄等措施来优化了性能(以后学习)。

  因为任何不从应用程序的根中访问的对象都会在某个时刻被收集,所以应用程序将不可能发生内存泄漏,另外应用程序也不可能再访问已经被释放的对象。因为如果对象可达,它将不可能被释放;而如果对象不可达,应用程序必将无法访问到它。

  下面代码演示了垃圾收集器是这样分配管理对象的:

class Program
{
static void Main( string [] args)
{
// 在托管堆上ArrayList对象,a现在就是一个根
ArrayList a = new ArrayList();

// 在托管堆上创建10000个对象
for ( int i = 0 ; i < 10000 ; i ++ )
{
a.Add( new Object()); // 对象被创建在托管堆上
}
// 现在a是一个根(位于线程堆栈上)。所以a是一个可达对象
// ,a引用的10000个对象也是可达对象
Console.WriteLine(a.Count);

// 在a.Count返回后,a便不再被Main中的代码所引用,
// 因此也就不再是一个根。如果另外一个线程在a.Count的结果被
// 传递给WirteLine之前启动了垃圾收集,那么a以及它所引用的10000个对象将会被回收。
// 上面for里面的变量i虽然在后面的代码中不再被引用,但由于它是一个值类型,并不存在于
// 托管堆中,所以它不受垃圾收集器的管理,它在Main方法执行完毕后会随着堆栈的消失而自动
// 被系统回收
Console.WriteLine( " End of method " );
}
}

 

  CLR之所以能够使用垃圾回收机制,有一个原因是因为托管堆总是能知道一个对象的实际类型,从而使用其元数据信息来判断一个对象的那些成员引用着其他对象。

 

------------------------------------------------------------------------------------------------------------

关于根的问题,好多朋友都讨论了,其实书上已有更详细的东西呢,只是那些000的东西我比较反感,非常抱歉,现在我贴出来,一起再学习一下:

  当JIT编译器编译一个方法的IL代码时,除了产生本地CPU代码外,JIT编译器还会创建一个内部逻辑表。从逻辑上来看,该表中的每一个条目都标识着一个方法的本地CPU指令的字节偏移范围,以及该范围中一组包含根的内存地址(或者CPU寄存器),下表描述了该内存表:

起始字节偏移                                              结尾字节偏移                               根                                                              
0x000000000x00000020this,arg1,arg2,ECX,EDX
0x000000210x00000122this,arg2,fs,EBX
0x000001230x00000145fs

  如果在0x00000021和0x00000122之间的代码执行时开始执行垃圾收集,那么垃圾收集器将知道参数this参数arg2,本地变量fs以及寄存器EBX都是根,他们引用的托管堆中的对象将不会被认为是可收集的垃圾对象。除此之外,垃圾收集器还可以遍历线程的调用堆栈,通过检测其中每一个方法内部表来确定所有调用方法中的根,最后,垃圾收集器使用其他一些手段获得存储在全局引用类型变量和静态引用类型变量中保持的根。

  在上表中方法的arg1参数在偏移为0x00000020处的指令执行完毕后就不再被引用了,这意味着arg1引用的对象在该指令执行后的任何时刻都可以被垃圾收集器收集(假设应用程序中没有其他的根再引用该对象),换句话说,只要一个对象不再可达,它就是垃圾收集器的候选对象,CLR并不保证对象在一个方法的整个生存期内都一直存活。

  另外请大家关注11楼的答复。

一本书不要指望一次就能看懂啊

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

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

相关文章

gperftools mysql_利用 gperftools 对nginx mysql 内存管理 性能优化

利用 gperftools 对nginx 与 mysql 进行 内存管理 性能优化 降低负载.Gperftools 是由谷歌开发。官方对gperftools 的介绍为&#xff1a;These tools are for use by developers so that they can create more robust applications. Especially of use to those developing m…

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

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

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

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

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

接上一篇.net框架读书笔记---CLR内存管理\垃圾收集&#xff08;二&#xff09;&#xff0c;主要学习了终止化对象&#xff08;实现了Finalize方法的对象&#xff09;&#xff0c;了解了终止化对象的弊端&#xff0c;学习了通过实现IDisposable接口&#xff0c;通过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内存管理\垃圾收集(四)

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

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内存管理\垃圾收集(五)

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

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

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

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

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

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

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

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

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

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

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

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

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

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)&#xff0c;但是为了优化应用程序性能我们始终需要了解内存管理和垃圾回收(GC)。另外&#xff0c;了解内存管理可以帮助我们理解在每一个程序中定义的每一个变量是怎样工作的。 简介 继续上篇未完成的“参数传…

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)&#xff0c;但是为了优化应用程序性能我们始终需要了解内存管理和垃圾回收(GC)。另外&#xff0c;了解内存管理可以帮助我们理解在每一个程序中定义的每一个变量是怎样工作的。 简介 这一节我们将介绍引用类型…

linux virt java_Linux下Java环境安装

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

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

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