.Net垃圾回收

原文作如下总结。

简介

.Net垃圾回收器管理应用程序的内存分配和释放。 每当有对象新建时,公共语言运行时都会从托管堆为对象分配内存。 只要托管堆中有地址空间,运行时就会继续为新对象分配空间。 不过,内存并不是无限的。 垃圾回收器最终必须执行垃圾回收来释放一些内存

一些基础

  1. 公共语言运行时 (CLR) 中,垃圾回收器用作自动内存管理器。

  2. 作为一名应用程序开发人员,<u style="box-sizing: border-box;">你只能使用虚拟地址空间,请勿直接操控物理内存</u>。 垃圾回收器为你分配和释放托管堆上的虚拟内存。

    虚拟内存分为

    • 可用(即可分配)

    • 保留(即已分配)

    • 提交(即已指派给物理内存)三种状态。

  3. 发生垃圾回收的三种情况:

    • 物理内存不够了

    • 托管堆上已分配的对象使用的内存过高

    • GC.Collect被调用。

  4. 在垃圾回收器由 CLR 初始化之后,它会分配一段内存用于存储和管理对象。 此内存称为托管堆(与操作系统中的本机堆相对):一个用于小型对象(小型对象堆或 SOH),一个用于大型对象(大型对象堆)。每个托管进程都有一个托管堆。 进程中的所有线程都在同一堆上给对象分配内存。

托管堆视为两个堆的累计:大对象堆和小对象堆。

  1. 垃圾回收器分配的段大小是动态的。 应用程序不应假设特定段的大小或依赖于此大小,也不应尝试配置段分配可用的内存量。

  2. 堆以代数(共3代)来组织,这样就能有效管理短生存对象和长生存对象。垃圾回收最常发生在第0代(包含短生存期对象),当回收第2代时会连带它所有代一起回收。垃圾回收中未回收的对象也称为幸存者,并会被提升到下一代。

  3. 垃圾回收过程中发生了什么:

    • 标记阶段,找到并创建所有活动对象的列表。

    • 重定位阶段,用于更新对将要压缩的对象的引用。

    • 压缩阶段,用于回收由死对象占用的空间,并压缩幸存的对象。

  4. 垃圾回收器使用以下信息来确定对象是否为活动对象

    • 堆栈根。 由实时 (JIT) 编译器和堆栈查看器提供的堆栈变量。 请注意,JIT 优化可以延长或缩短报告给垃圾回收器的堆栈变量内的代码的区域。

    • 垃圾回收句柄。 指向托管对象且可由用户代码或公共语言运行时分配的句柄。

    • 静态数据。 应用程序域中可能引用其他对象的静态对象。 每个应用程序域都会跟踪其静态对象。

      在垃圾回收启动之前,除了触发垃圾回收的线程以外的所有托管线程均会挂起。

大型对象堆(LOH)

如果对象大于或等于 85,000 字节,将被视为大型对象。 此数字根据性能优化确定。 对象分配请求为 85,000 字节或更大时,运行时会将其分配到大型对象堆。

小型对象始终在第 0 代中进行分配,或者根据它们的生存期,可能会提升为第 1 代或第 2 代。 大型对象始终在第 2 代中进行分配。大型对象属于第 2 代,因为只有在第 2 代回收期间才能回收它们。 回收一代时,同时也会回收它前面的所有代。

执行GC的情形

  • 分配超出第 0 代或大型对象阈值。

    阈值是某代的属性。 垃圾回收器在其中分配对象时,会为代设置阈值。 超出阈值后,会在该代上触发 GC。

    这是典型情况,大部分 GC 执行都因为托管堆上的分配。

  • 调用 GC.Collect 方法。

    如果调用无参数 GC.Collect() 方法,或另一个重载作为参数传递到 GC.MaxGeneration,将会一起收集 LOH 和剩余的托管堆。

  • 系统处于内存不足的状况。

    垃圾回收器收到来自操作系统 的高内存通知时,会发生以上情况。 如果垃圾回收器认为执行第 2 代 GC 会有效率,它将触发第 2 代。

手动清理非托管资源

对于应用创建的大多数对象,可以依赖 .NET 的垃圾回收器来处理内存管理,释放托管资源。 但是,如果创建包括非托管资源的对象,则当你在应用中使用完非托管资源后,必须显式释放这些资源。 最常用的非托管资源类型是包装操作系统资源的对象,如文件、窗口、网络连接或数据库连接。 虽然垃圾回收器可以跟踪封装非托管资源的对象的生存期,但无法了解如何发布并清理这些非托管资源。 如果你的类型使用非托管资源,则应执行以下操作:

  1. 实现释放模式。这就需要你去继承并实现 IDisposable.Dispose方法,来实现对非托管资源的明确性释放。这样当不再使用此对象时,类的使用者可以调用该方法释放资源。

  2. 如果上述情况下对象引用忘记调用 Dispose方法,你得为释放非托管资源做准备。方法有两种:

  • 使用安全句柄包装非托管资源(推荐)。安全句柄派生自System.Runtime.InteropServices.SafeHandle 类并包含可靠的 Finalize 方法:System.Object声明的一个虚方法Finalize(也称为终结器),它在GC回收对象的内存之前由GC调用,并且可以被覆盖以释放非托管资源。 】。 在使用安全句柄时,只需实现 IDisposable 接口并在 Dispose 实现中调用安全句柄的 IDisposable.Dispose 方法。 如果未调用安全句柄的Dispose 方法,则垃圾回收器将自动调用安全句柄的终结器。

  • 重写 Object.Finalize 方法。 当类型使用者无法调用 IDisposable.Dispose以确定性地释放非托管资源时,终结器会启用对非托管资源的非确定性释放。 但是,由于对象终止是一项复杂且易出错的操作,建议你使用安全句柄而不是提供你自己的终结器。

完成上述内容后,对象引用就可以直接调用 IDisposable.Dispose 实现以释放非托管资源使用的内存。另外值得注意的是, 在正确实现 Dispose 方法后,安全句柄的 Finalize 方法或 Object.Finalize方法的重写会在未调用 Dispose 方法的情况下阻止清理资源。

GC优缺点

在公共语言运行时 (CLR) 中,垃圾回收器用作自动内存管理器。 它提供如下优点:

  • 使你可以在开发应用程序时不必释放内存。

  • 有效分配托管堆上的对象。

  • 回收不再使用的对象,清除它们的内存,并保留内存以用于将来分配。 托管对象会自动获取干净的内容来开始,因此,它们的构造函数不必对每个数据字段进行初始化。

  • 通过确保对象不能使用另一个对象的内容来提供内存安全。

缺点:

开销影响程序性能。

减少GC开销的策略

  • 只在必要时显示调用GC

  • 尽量减少临时对象的使用(循环中的临时变量、对象缓存)

  • 对象不用最好显式置为null(即时置空对资源的引用)

  • 字符串拼接使用StringBuilder

  • 能用基本值类型就不用引用类型

  • 尽量减少不必要的静态变量

  • 分散对象的创建或删除时间

性能分析工具(待了解)

  • 内存计数器

  • SOS调试

  • 垃圾回收ETW事件

  • 分析API

  • 程序域监控

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容