孙广东  2017.3.27

http://blog.csdn.NET/u010019717

内容摘取自  CLR via C#     第21章 托管堆和垃圾回收

同时也推荐查看我之前转载的一篇好文章  《对比Ruby和Python的垃圾回收》

托管堆基础

资源包括包括:文件、内存缓冲区、网络连接等。

以下是访问一个资源所需的步骤:

  1. 调用IL指令newobj,为代表资源的类型分配内存(一般使用C# new操作符来完成)。
  2. 初始化内存,设置资源的初始状态并使资源可用。类型的实例构造器负责设置初始状态。
  3. 访问类型的成员来使用资源(有必要可以重复)。
  4. 摧毁资源的状态以进行清理。(为了简化编程,开发人员经常使用的大多数类型都不需要这个步骤)。
  5. 释放内存。垃圾回收器独自负责这一步。

托管堆分配资源

CLR要求所有对象都从托管堆分配。进程初始化时,CLR划出一个地址空间区域作为托管堆,CLR还要维护一个指针,我把它称作NextObjPtr。该指针指向下一个对象在堆中的分配位置。

你的应用程序的内存受进程的虚拟地址空间的限制。32位进程最多能分配1.5GB,64位进程最多能分配8TB。

C#的new操作符导致CLR执行以下步骤:

  1. 计算类型的字段(以及从基类型继承的字段)所需的字节数。
  2. 加上对象的开销所需的字节数。每个对象都有两个开销字段:类型对象指针和同步块索引。对于32位应用程序,这两个字段各自需要32位,所以每个对象都要8字节。对于64位应用程序,这两个字段各自需要64位,所以每个对象要增加16个字节。
  3. CLR检查区域中是否有分配对象所需的字节数。如果托管堆有足够的可用空间,就在NetxObjPtr指针指向的地址处放入对象,为对象分配的字节会被清零。接着调用类型的构造器(为this参数传递NextObjPtr),new操作符返回对象引用。就在返回这个对象引用之前,NextObjPtr指针的值会加上这个对象占用的字节数来得到一个新值,即下个对象放入托管堆时的地址。

垃圾回收算法

应用程序调用new操作符创建对象时,可能没有足够地址空间来分配该对象,发现空间不够,CLR就执行垃圾回收。

至于对象生存期的管理,有的系统采用的是某种引用计数算法。在这种系统中,堆上的每个对象都维护着一个内存字段来统计程序中多少“部分”正在使用对象。随着每一“部分”到达代码中某个不再需要对象的地方,就递减对象的计算字段。计数字段成0,对象就可以从内存中删除了。许多引用计数系统最大的问题是处理不好循环引用。

鉴于引用计数垃圾回收器算法存在的问题,CLR改为使用一种引用跟踪算法。引用跟踪算法中关心引用类型的变量,因为只有这种变量才能引用堆上的对象;值类型变量直接包含值类型实例。引用类型变量可在许多场合使用,包括静态和实例字段,或者方法的参数和局部变量。我们将所有引用类型的变量都称为根。

1、CLR开始GC时,首先暂停进程中的所有线程。这样可以防止线程在CLR检查期间访问对象并更改其状态。

2、然后,CLR进入GC的标记阶段。在这个阶段,CLR遍历堆中的所有对象,将同步块索引字段中的一位设为0。这表明所有对象都应删除。

3、然后,CLR检查所有的活动根,查看它们引用了哪些对象。这正是CLR的GC称为引用跟踪GC的原因。如果一个根包含NULL,CLR忽略这个根并继续检查下一个根。

4、任何根如果引用了堆上的对象,CLR都会标记那个对象,也就是将该对象的同步块索引中的位设为1, 标记过程会持续,直至应用程序的所有跟所有检查完毕。

5、检查完毕后,堆中的对象要么已标记,要么未标记。已标记的对象不能被垃圾回收,因为至少有一个根在引用它。我们说这种对象是可达的。因为应用程序代码可通过仍在引用它的变量抵达(访问)它。未标记的对象是不可达的。因为应用程序中不存在使对象能被再次访问的根。

6、CLR知道哪些对象可以幸存,哪些可以删除后,就进入GC的压缩(不是那个压缩,类似于碎片整理)阶段。在这个阶段。CLR对堆中标记的对象进行“乾坤大挪移”。

7、在内存中移动了对象之后有一个问题亟待解决。引用幸存对象的根现在引用的还是对象最初在内存中的位置,而非移动后的位置。被暂停的线程恢复执行时,将访问旧的内存位置,会造成内存损坏。这显然是不能容忍的,所以作为压缩阶段的一部分,CLR还要从每个根减去所引用对象在内存中偏移的字节数。这样就能保证每个根还是引用和之前一样的对象,只是对象在内存中变换了位置。

8、压缩阶段完成后,CLR恢复应用程序的所有线程。

重要提示:   静态字段引用的对象一直存在,直到用于加载类型的AppDomain卸载为止。内存泄漏的一个常见原因就是让静态字段引用某个集合对象,然后不停地往集合添加数据项。静态字段使集合对象一直存活,而集合对象使所有数据项一直存活。因此应该尽量避免使用静态字段。(或者参照前面的玩法,当我们不用静态变量的时候,可以立马置为null,那么垃圾就会被回收)。

代:提升性能

CLR的GC是基于代的垃圾回收器。它对代码做了如下假设:

  • 对象越新,生存期越短
  • 对象越老,生存期越长
  • 回收堆的一部分,速度快于回收整个堆

第一个假设是越新的对象活的越短。因此,第0代包含跟多垃圾的可能性很大,能回收更多的内存。由于忽略了第1代中的对象,所以加快了垃圾回收速度。

第二个假设越老的对象活的越长。也就是说,第1代对象在应用程序中很有可能继续可达(没被回收)的。如果垃圾回收器检查第1代中的对象,很有可能找不到多少垃圾。

由于第0代已满,所以必须开始垃圾回收。但这一次垃圾回收器发现第1代占用了太多内存,以至于用完了预算。 由于前几次对第0代进行回收时,第1代可能已经有许多对象变得不可达(该回收)。所以这次垃圾回收器决定检查第1代和第0代的所有对象。 两代都被垃圾回收后,  就出现了第2代了。 空的是0代, 0代幸存者变为1代,1代幸存者变为2代。

托管堆只支持三代:第0代, 第1代,第2代。

CLR 的垃圾回收器是自动调节的:

1、如果垃圾回收器发现在回收第0代后存活下来的对象很少,就可能减少第0代的预算。已分配空间的减少意味着垃圾回收将更频繁地发生。

2、另一方面,如果垃圾回收器回收了第0代,发现还有很多对象存活,没有多少内存被回收就会增加第0代的预算。

3、垃圾回收器用类似的  启发式算法 调整第1代 和 第2代的预算。

垃圾回收触发条件

1、CLR在检测第0代超过预算时会触发一次GC,这是GC最常见的触发条件,还有其它的触发如下:

2、代码显示调用System.GC的静态Collect方法,  大多时候都要避免调用这个方法;最好让垃圾回收器自行斟酌执行,让它根据应用程序的行为调整各个代的预算。

3、Windows报告低内存情况

4、CLR正在卸载AppDomain

5、CLR正在关闭

官方的介绍:(良心发现 有中文)

https://docs.microsoft.com/zh-cn/dotnet/articles/standard/garbagecollection/

还想深入了解GC 的 可以看看这本书《

垃圾回收的算法与实现

《读书笔记》C#/.Net 的托管堆和垃圾回收相关推荐

  1. 【CLR】解析CLR的托管堆和垃圾回收

    目录结构: contents structure [+] 为什么使用托管堆 从托管堆中分配资源 托管堆中的垃圾回收 垃圾回收算法 代 垃圾回收模式 垃圾回收触发条件 强制垃圾回收 监视内存 对包装了本 ...

  2. .net C# 堆 栈 垃圾回收 GC

    .NET C# .NET C# .NET C# .NET C# .NET C# .NET C# .NET C# 栈 堆 垃圾回收 GC #1 尽管在.NET framework下我们并不需要担心内存管 ...

  3. 周志朋java_java中堆和垃圾回收机制的介绍

    java中堆和垃圾回收机制的介绍 发布时间:2020-07-03 14:55:53 来源:亿速云 阅读:113 作者:元一 这篇文章将为大家详细讲解有关java中堆和垃圾回收机制的介绍,文章内容质量较 ...

  4. JVM内存结构之堆的垃圾回收过程

    前言: 上一篇文章介绍了简单的JVM内存结构,然后本篇重点解释堆区域的垃圾回收清理过程(jdk1.8),为什么说堆呢?因为垃圾回收的核心就是堆区域来做的,像栈区域执行完就会把线程释放掉.方法区(元空间 ...

  5. C#垃圾回收学习总结

    浅谈C#垃圾回收 http://www.cnblogs.com/cuiyiming/archive/2013/03/26/2981931.html 理解C#垃圾回收机制我们首先说一下CLR(公共语言运 ...

  6. Java堆的管理--垃圾回收

    1  引言 Java的堆是一个运行时数据区,类的实例(对象)从中分配空间.Java虚拟机(JVM)的堆中储存着正在运行的应用程序所建立的所有对象,这些对象通过new.newarray.anewarra ...

  7. .Net 垃圾回收和大对象处理

    英文原文:Maoni Stephens,编译:赵玉开(@玉开Sir) CLR垃圾回收器根据所占空间大小划分对象.大对象和小对象的处理方式有很大区别.比如内存碎片整理 -- 在内存中移动大对象的成本是昂 ...

  8. 关于.net的垃圾回收和大对象处理_标记

    CLR垃圾回收器根据所占空间大小划分对象.大对象和小对象的处理方式有很大区别.比如内存碎片整理 -- 在内存中移动大对象的成本是昂贵的,让我们研究一下垃圾回收器是如何处理大对象的,大对象对程序性能有哪 ...

  9. .NET 垃圾回收与内存泄漏

    .NET 垃圾回收与内存泄漏 原文:.NET 垃圾回收与内存泄漏 > 前言 相信大家一定听过,看过甚至遇到过内存泄漏.在 .NET 平台也一定知道有垃圾回收器,它可以让开发人员不必担心内存的释放 ...

最新文章

  1. 委托、事件、事件访问器
  2. 中国科学家发现恐惧情绪的新环路
  3. 让nginx支持php和path_info(新版本)
  4. Linux 爱好者该向闭源软件敞开怀抱了
  5. 【Flask】自定义转换器
  6. mongodb 的使用场景和不适合使用的场景
  7. MySQL TEXT数据类型的最大长度
  8. 最详细的最小堆构建、插入、删除的过程图解
  9. Android之matrix类控制图片的旋转、缩放、移动
  10. jvm高并发_JVM上的高并发HTTP客户端
  11. ceph rgw java_ceph rgw multisite基本用法
  12. php 计算 目录大小,php计算整个目录大小的方法
  13. warshall算法求传递闭包c++_【建模小课堂】图论算法
  14. linux怎么改目录位置,Linux下更改MySQL数据目录位置具体操作方法
  15. 牛客网Java刷题知识点之调用线程类的start()方法和run()方法的区别
  16. NumPy的详细教程
  17. numpy缩放图片/调整图片大小
  18. 体育用品商城小程序开发功能
  19. google阅读器快捷键
  20. SLAM④----李群与李代数

热门文章

  1. JHOK-ZBG1,JHOK-ZBG2漏电继电器
  2. 浙大中控ECS700学习笔记一硬件
  3. 摆臂式垃圾车18金字塔下拉词
  4. centos7 目录扩容 LVM
  5. 不用编程超简单的自动化测试工具:Airtest安装使用入门篇
  6. P4编程理论与实践——理论篇(转载)
  7. 多线程python_python高级之多线程
  8. AI芯片的分类及应用
  9. MySQL计算两个日期相差天数
  10. c语言结题报告事例,STITP结题报告.PDF