IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> CLR的托管堆和垃圾回收:垃圾回收算法 -> 正文阅读

[C++知识库]CLR的托管堆和垃圾回收:垃圾回收算法

书接上文,基于前面的描述,似乎托管对的性能天下无敌。但先别激动,刚才说的有一个大前提-内存无限,CLR总是能分配新对象。但内存不可能时无限的,所以CLR通过称为“垃圾回收”(GC)的技术"删除"堆中你的应用程序不在需要的对象。

垃圾回收算法

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

事实上,垃圾回收是在第0代满的时候发生的。本章后面会解释'代',故我们先假设堆满就发生垃圾回收

对象生存期的管理

有的系统采用的是某种引用计数法。事实上,Microsoft自己的“组件对象模型”(Component Object Model,COM)用的就是引用计数。在这种系统中,堆上的每个对象都维护着一个内存字段来统计程序中多少“部分”正在使用对象。随着每一“部分”到达代码中某个不再需要对象的地方,就递减对象的技术字段。技术字段编程0,对象就可以从内存中删除了。许多引用计数系统最大的问题是处理不好循环引用。例如在GUI应用程序中,窗口将容纳对子UI元素的引用,而子UI元素将容纳父窗口的引用。这种引用会阻止两个对象的计数器达到0,所以两个对象永远不会删除,即使应用程序本身不再需要窗口。

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

CLR开始GC时,首先暂停进程中的所有线程。这样可以防止线程在CLR检查期间访问对象并更改其状态。然后,CLR进入GC的标记阶段。在这个阶段,CLR遍历堆洪的所有对象,将同步块索引字段中的一位设为0。这表明所有对象都应删除。然后,CLR检查所有活动根,查看它们引用了哪些对象。这正是CLR的GC称为引用跟踪GC的原因。如果一个根包含null,CLR忽略这个根并继续检查下一个根。

任何根如果引用了堆上的对象,CLR都会标记这个对象,也就是将该对象的同步块索引中的位设为1。一个对象被标记后,CLR会检查那个对象中的根,标记它们引用的对象,如果发现对象已经被标记,就不重新检查对象的字段。这就避免了因为循环引用而产生的死循环。

下图展示了一个堆,其中包含几个对象。应用程序的根直接引用对象A,C,D和F。所有对象都已标记。标记对象D时,垃圾回收器发现这个对象含有一个引用对象H的字段,造成对象H也被标记。标记过程会持续,直至应用程序的所有根所有检查完毕。

在这里插入图片描述

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

CLR知道哪些对象可以幸存,哪些可以删除后,就进入GC的压缩(compact)阶段。在这个阶段,CLR对堆中被标记的对象进行“乾坤大挪移”,压缩所有幸存下来的对象,使他们占用连续的内存空间。这样做有许多好处。

  • 首先,所有幸存对象在内存中紧挨在一起,恢复了引用的“局部化”,减小了应用程序的工作集,从而提升了将来访问这些对象的性能。
  • 其实,可用空间也全部使连续的,所以这个地址空间区段得到了解放,允许其他东西进驻。
  • 最后,压缩意味着托管堆解决了本机(原生)堆的空间碎片化问题。

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

压缩好内存后,托管堆NextObjPtr指针指向最后一个幸存对象之后的位置。下一个分配的对象将放在这个位置,下图展示了压缩i二段之后的托管堆。压缩阶段完成后,CLR恢复应用程序的所有线程。这些线程继续访问对象,就好像GC没有发生过一样 。

在这里插入图片描述

如果CLR在一次GC之后回收不了内存,而且进程中没有空间来分配新的GC区域,就说明该进程的内存已耗尽。此时,试图分配更多内存的new操作符会抛出一个经典错误OutOfMemoryException。应用程序可捕捉该异常并从中恢复。但大多数应用程序都不会这么做;相反,异常会称为未处理异常,Windows将种植进程并回收进程使用的全部内存。

作为程序员,应该主义开头描述的两个bug不复存在了。

  • 首先,内存不可能泄露,因为从应用程序的根访问不了任何对象,都会在某个时间被垃圾回收。
  • 其次,不可能因为访问被释放的内存而造成内存损坏,因为现在只能引用活动对象;非火哦东的对象时引用不了的。

问被释放的内存而造成内存损坏,因为现在只能引用活动对象;非火哦东的对象时引用不了的。

静态字段引用的对象一直存在,直到用于加载类型的AppDomain卸载为止。内存泄漏的一个常见原因就是让静态字段引用某个集合对象,然后不停地向集合中添加数据项。静态字段使集合对象一直存活,而集合对象使所有数据项一直存活。因此,应尽量避免使用静态字段。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-12 17:14:26  更:2022-03-12 17:17:33 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 4:58:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码