1、托管堆基础

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

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

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

2、托管堆分配资源

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指针的值会加上这个对象占用的字节数来得到一个新值,即下个对象放入托管堆时的地址。

3、垃圾回收算法

应用程序调用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,那么垃圾就会被回收)。

4、代:提升性能

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代的预算。

5、垃圾回收触发条件

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/

6、 扩展阅读

unity在线文档Understanding the managed heap

https://docs.unity3d.com/Manual/BestPracticeUnderstandingPerformanceInUnity4-1.html

Optimizing garbage collection in Unity games

https://unity3d.com/cn/learn/tutorials/topics/performance-optimization/optimizing-garbage-collection-unity-games?playlist=44069

C# Garbage Collection Tutorial

https://stackify.com/c-garbage-collection/

Memory Management/Stacks and Heaps

https://en.wikibooks.org/wiki/Memory_Management/Stacks_and_Heaps

Lambda Expressions vs. Anonymous Methods

https://blogs.msdn.microsoft.com/ericlippert/2007/01/10/lambda-expressions-vs-anonymous-methods-part-one/

推荐书籍:

《垃圾回收的算法与实现》

《C#/.Net 的托管堆和垃圾回收》

《CLR via C#》

转载自:https://blog.csdn.net/u010019717/article/details/66975553

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u010019717/article/details/66975553

C#/.Net 的托管堆和垃圾回收相关推荐

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

    孙广东  2017.3.27 http://blog.csdn.NET/u010019717 内容摘取自  <CLR via C#>     第21章 托管堆和垃圾回收 同时也推荐查看我之 ...

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

最新文章

  1. 双料状元收割神器来了!清华设立计算机金融双学士学位
  2. 【渝粤教育】国家开放大学2018年春季 7405-21T面向对象程序设计(本) 参考试题
  3. Oracle Stream配置详细步骤
  4. Cache related website
  5. nacos修改端口号
  6. java web代码混淆_JAVA WEB 项目的代码混淆
  7. MP288MP280清零软件
  8. 【KITTI】KITTI数据集简介(一) — 激光雷达数据
  9. 第一次做APP接口开发过程总结
  10. 央视财经采访:康晓阳投资分享
  11. 在开发中如何正确的应对系统故障
  12. 如何下载网页中的视频?
  13. java导出pdf 含图片_【Java】itext根据模板生成pdf(包括图片和表格)
  14. 职言 | 单纯做业务测试真的行得通吗?
  15. 超级电容-一阶RC模型-RLS参数辨识-Simulink仿真
  16. 评论回复功能 asp.net_微信重大更新!公众号推送时间线打乱+7大新功能上线!怎么玩?...
  17. C语言:最大公约数。
  18. c语言程序风光互补系统论文,风光互补控制器设计及实验平台搭建
  19. python去除excel空行_python中删除列表中的空元素以及如何读取excel中的数据
  20. 编写使用systemctl管理的service服务

热门文章

  1. 实变函数与计算机有关系吗,《复变函数与实变函数》
  2. 如何写一份优秀的Web前端简历【面试秘籍】
  3. 【MATLAB】QPSK调制与解调在MATLAB平台上的实现
  4. 西门子S7-200PLC基本入门编程
  5. java 24种设计模式
  6. 如何记账,教你在同一天记录多笔收支情况
  7. SpringBoot项目对接短信平台Demo
  8. “华为杯”第17届中国研究生数学建模竞赛B题二等奖论文
  9. linux uvc协议_linux uvc 深入理解(一)
  10. python分析财务报表