【翻译】CLR 自动内存管理

自动内存管理是 CLR 在执行托管代码过程中提供的一项功能。CLRGC (Garbage Collection, 垃圾回收器, 下同)可以自动帮助程序分配或释放内存。对于开发者来说,这意味着开发者并不需要写额外的代码来处理内存管理的事情了,同时这也避免了比较常见的错误,比如:

  • 忘记释放内存
  • 释放已经释放过的内存
  • 访问一个已经被释放的内存

本文将会讨论 GC 在分配和释放内存时是如何工作的。

分配内存


当建立一个新的线程时,CLR 将会为该线程准备一个连续的内存区域——托管堆。托管堆将会维护一个指针,该指针的地址将会是下一个对象生成的地址。在托管堆刚生成时,该地址会指向整个堆的基地址。所有的引用类型将会在堆中分配内存。程序生成第一个引用类型对象的时候,该类型的内存将会被分配在托管堆的基地址中。当程序创建下一个对象的时候,GC 会立即在第一个对象的后面分配一段内存,以此类推。

托管堆的内存分配比非托管的内存分配快,托管堆在分配内存时,仅相当于给一个指针加了一个数而已,此过程就像在栈中分配内存一样快。不仅如此,由于托管堆中内存分配是连续的,并且对象存储的位置也是连续的,这样做会加快程序访问对象的速度。

释放内存


GC 的优化引擎将会针对程序选择一个最佳的时间来进行垃圾回收操作。回收内存的时候,程序不用的对象将会被回收。GC 将会检查程序的“根”。每一个托管的应用程序都有一个一组“根”。每一个根都会存一个托管堆中对象的引用,或者被设置为null。程序的根包括static 成员、线程栈中的局部变量与方法的参数、CPU 寄存器。GC 将会拿到一个根的列表,其中包括被 JIT 激活的根和 正在被 CLR 维护的根。GC将会根据这个列表生成一个图,这个图里包含所有可以从根访问到的对象。

如上文所述,不在图中的对象将不会被程序的 “根” 引用,GC 将考虑回收这些对象。在回收的过程中,GC 会先检查托管堆,找到所有无法访问到的对象;之后,GC 会使用内存拷贝功能来整理所有正常的对象,让它们仍在在一个连续的内存空间内;最后一步很重要,GC 会更正程序中的“根”,并且将托管堆中的指针指向最后一个对象的后面。注意,只有当不可访问对象到达一定数量的时候才会进行内存整理的操作。如果托管堆中所有的对象均能正常访问,整理内存的操作是没有必要的。

为了提升性能,运行时会把一个大对象拆分到两个堆中存放,并且,GC将会自动释放大对象的内存。然而,为了防止移动内存中的大对象,这种情况下的内存是未经整理的。

托管堆的生命周期(Generations)和性能


为了优化 GC 的性能,托管堆中的空间被分为三个部分:G0generation 0,下同),G1G2CLR 采用的 GC 方案已经涵盖了软件行业中的各种情况。该方案基于以下前提:

  • 在回收托管堆时,只回收其中一部分内存比回收全部内存快
  • 较新的对象通常比较老的对象的寿命短
  • 较新的对象之间有在彼此之间产生联系的倾向,并几乎在同一时间被程序引用。

GC 会把较新的对象放在 G0 中。在程序运行的初期结束后,G0 中未被回收的对象将会被“升级”,并被移动至 G1G2(对象升级的过程将会在下文中讨论)。由于回收部分内存较快,托管堆将会优先回收具体的某一个时期(Generation),而不是回收一整个托管堆。

CLR 实际工作中,GC 将会在 G0 没有足够空间时回收内存。当程序在 G0 满了的时候尝试创建对象时,GC 发现 G0 中没有空间了,所以它将会尝试回收 G0 中的空间。
由于最先创建的对象寿命通常会比较短,故G0 中的对象会有更大的几率被回收,所以我们并没有直接回收整个托管堆的空间,这么做是一种比较高效的方式,在 G0 中进行局部的内存回收通常会释放足够的空间给新创建的对象。

在回收 G0 之后,GC 将会整理托管堆中的内存本文“释放内存”一章提到过。由于已经留存下来的对象更倾向于有较长的生命周期,所以 GC 要把 G0 中的对象提升至更高的时期(genearation)中。所以,GC也没有必要在整理 G0 之后再去检查 G1G2 中的对象了。

GC 整理完 G0 并且将其中的对象提升至 G1 之后,它将会使用 G0 中剩余的空间来创建新的对象,直到 G0 中又没有剩余空间时,GC 需要决定是否需要回收较老的时期(generation)中的对象。比如,如果 GCG0 中无法回收足够的空间去创建新的对象时,GC 将会先后在 G1G2 中触发 垃圾回收的过程。如果这么做仍然无法满足新创建对象的需求时,GC 将会回收 G2G1G0 中的内存,并且将 G0 中的剩余对象全部升级到 G1 中,G1 中剩余的对象全部升级到 G2 中。由于垃圾回收只支持三个时期(generations),所有 G2 中的对象将会继续呆在 G2 中,直到其中的对象变得不可到达时,才回收这些对象。

给非托管代码释放内存


应用程序生成的对象都可以依赖 GC 来完成自动垃圾回收。然而,非托管资源需要自己手动管理对象的生命周期。典型的非托管资源的应用就是文件句柄file handle,窗口句柄window handle,或者网络操作。即使 GC 可以追踪包含非托管对象的托管对象,但是它并不知道到底如何清理非托管的对象。

当你试图在托管对象中中引用非托管的对象时,应该自己实现当前对象的Dispose方法,并且在该方法中清理非托管的对象。你可以在该对象的使用者结束使用该对象的时候释放分配给该对象的内存。当使用包含非托管资源的托管对象时,一定要在必要的时候调用Dispose方法以确保该托管对象已经被释放。关于如何实现Dispose方法,你可以参照 垃圾回收 一文

翻译错误之处,请指出,感激不尽~

原文地址

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

推荐阅读更多精彩内容