CLR via C#-托管堆和垃圾回收
创始人
2024-03-22 17:52:22
0

目的:
如何构造新对象,托敢对如何控制这些对象的生存期,以及如何管理这些对象的内存


托管堆基础
每个程序都需要使用资源,比如:文件、内存缓冲区、屏幕空间、网络连接、数据库资源等等
面向对象编程中,每个类型都是一种资源,如何访问资源?
a.调用 IL 指令 newobj,为代表资源的类型分配内存
b.初始化内存,设置资源的初始化状态并使用(每个类型实例构造器负责设置初始状态)
c.访问类型的成员来使用资源
d.摧毁资源的状态进行清理(unsafe资源需要)
e.释放内存。垃圾回收器负责

如何从托管堆分配内存?
CLR 要求所有的对象都从托管堆分配,进程初始化时 CLR 划出一个地址空间区域作为托管堆
CLR 维护一个指针(NextObjPtr),指向下一个对象在堆中分配位置
(32位进程最多能分配1.5G,64位进程最多能分配8TB)
c# 的 new 操作:
a.计算类型的字段(包括基类继承的)需要的字节数
b.加上对象的开销所需的字节数,每个对象有两个开销字段:类型对象指针和同步块索引
c.CLR 检查区域中是否有分配对象所需的字节数,如果有的话就控制 NextObjPtr 指针指向的地址放入对象,为对象分配的字节会被清零。接着调用类型的构造器,new 操作符返回对象的引用。
同时 NextObjPtr 会加上对象所占用的字节数,给下一个对象做初始位置(连续分配可以提高缓存命中)
d.如果没有就进行垃圾回收

垃圾回收的算法是啥?
微软用的是 组件对象模型(Component Object Model,COM)也是引用计数
堆上的每个对象都维护者一个内存字段统计程序中多少地方引用这个对象,当不需要这个对象的时候就会递减计数,直到0的时候,就可以从内存中删除了
注意:循环引用问题,会导致两个对象永远不会删除

CLR 改为使用一种引用跟踪算法,值关心引用类型的变量,只有这种变量才可以引用堆上的对象
(而值类型变量直接包含值类型实例)
将引用类型的变量成为根

当CLR 开始 GC 时,流程如下:
a.暂停进程中的所有线程(防止线程在 CLR 检查期间访问对象并改期状态)
b.进行 GC 的标记阶段,会遍历堆中的所有对象,将同步块索引字段中的一位设置为0
c.检查活动根,查看它们引用了那些对象(引用跟踪)

  • 如果根包含 null ,就忽略这个
  • 如果根引用了堆上的对象,CLR 就会标记那个对象,将该对象同步块索引中的为设置为1。当某个对象被标记后,CLR 会检查那个对象中的跟,标记他们引用的对象。如果发现对象已经标记,就不重复检查,防止循环引用造成死循环
  • 检查完所有后,已标记的对象不能被垃圾回收,叫可达的(reachable),未标记的对象是不可达的(unreachable)

d.进入 GC 压缩(compact)阶段,CLR 对堆中已标记的对象占用连续的内存空间,解决内存碎片问题。还要从每个根对象减去所引用的对象的偏移字节数,保证和之前的引用是一样的
e.NextObjPtr 指向最后一个幸存对象之后的位置
f.恢复所有的线程

注意:静态字段引用的对象将一直存在,直到加载类型的 AppDomain 卸载位置。


下面这段代码,TimerCall 只会输出一次

public static class Program
{public static void Main(){Timer t = new Timer(TimerCallback, null, 0, 2000);Console.ReadLine();}static void TimerCallback(Object o){Console.WriteLine("In TimerCallback: " + DateTime.Now);GC.Collect();}
}

因为没有任何变量引用 Timer
切换到 debug 编译器模式,编辑器会将所有根的方法生存期延长至方法结束


如果切换到 -ebug编译模式,下面这段代码也只会输出一次
因为 t=null 被JIT 编译成等价于不引用该变量,会把整行代码注释掉

public static class Program
{public static void Main(){Timer t = new Timer(TimerCallback, null, 0, 2000);Console.ReadLine();t = null;}
}

如何不开启 debug 编译选项,还可以等待程序结束时再释放?
通过 t.Dispose() 这样会标记为 t 引用的对象必须存活才可以调用 Dispose()

public static class Program
{public static void Main(){Timer t = new Timer(TimerCallback, null, 0, 2000);Console.ReadLine();t.Dispose();}
}


CLR 的 GC 是基于代的垃圾回收器(generational garbage collector)
大部分项目都有如下特征:
a.对象越新,生存期越短
b.对象越老,生存期越长
c.回收堆的一部分,速度快于回收整个堆

托管堆在初始化时不包含对象,第一批添加到堆的对象成为第0代对象
CLR 初始化的时候为第0代对象选择一个预选容量,如果分配一个新对象造成的第0代超过预算,就必须启动一次垃圾回收

所有没被回收第0代的对象在被压缩之后变成1代对象,此时第0代就没有对象了
之后在申请内存就会被分配到第0代中,再选择新的预算空间(动态分配的)

之后再回收的时候,只会检测第0代的对象,其他代的对象采用标志位,如果修改了标识位就发生变化了,才会和第0代一起检测

所以,
第X代,就是经历过几次检查存活下的那一堆对象
托管堆只支持3代:第0代、第1代、第2代。没有第3代

注意:CLR 对大对象(超过85000字节)的对象特殊处理
a.不在小对象的地址空间分配,有专门的地方
b.不压缩大对象,因为移动的代价比较高
c.大对象始终是第2代,一般都是需要长时间存活的对象

垃圾回收的触发条件
a.第0代超过预算时
b.主动调用 GC.Collect
c.Windows 低内存
d.CLR 卸载AppDomain
e.CLR 正在关闭

相关内容

热门资讯

什么是价值判断与价值判断? 什么是价值判断与价值判断?价值判断:就是人们对事物能否满足主体的需要以及满足的程度作出判断;价值选择...
82年天龙八部康敏扮演者 82年天龙八部康敏扮演者林健明.... 康敏王爱明.... 天山童姥李琳琳.... 王夫人萧亮......
爱情诗的书 爱情诗的书有哪些爱情诗的书很不错啊?推荐几本。~柳永、秦观、崔护等的诗集。太多的理想主义,不忍深读。...
求小说名, 求小说名,邪王嗜宠:鬼医狂妃穿越小奶团哪种类型?这是第二问吧,
关于动物的童谣 关于动物的童谣关于动物的童谣:1、胖子和瘦子有个小孩叫胖子,割草丢了灰兔子;有个小孩叫瘦子,玩水丢了...
身上带着两个桃核可以辟邪吗? 身上带着两个桃核可以辟邪吗?核桃不辟邪的。可以去市场买对桃木雕刻的。另外东北有一种楸子核桃,按照中间...
急求嗟来之食辩论会资料 反方—... 急求嗟来之食辩论会资料 反方——该吃嗟来之食买一本教材全解,上面多的是,以下都是教材全解上的 :
我喜欢的人 我喜欢的人我有个喜欢的男生,听别人说他也喜欢我,可是我却感觉不到他喜欢我男孩子的心思没有那么简单的,...
模仿诗句 模仿诗句走啊,去看海,海是我们的梦走啊,去踏青,原野是我们的画布
阎立本的故居在哪里 阎立本的故居在哪里阎立本(约601年~673年), 唐代画家,官至宰相,汉族,雍州万年(今陕西省西安...
谁能告诉我恶魔奶爸讲得是什么啊 谁能告诉我恶魔奶爸讲得是什么啊不就是捡到个婴儿,是魔王的儿子,魔王派这个刚出生的婴儿来毁灭人类,但来...
酣畅淋漓 近义词 酣畅淋漓 近义词【成语】: 酣畅淋漓【近义词】: 淋漓尽致【反义词】: 扦格不通、诘屈聱牙【拼音】:...
张鲁一、倪妮首次合作电影《漫长... 张鲁一、倪妮首次合作电影《漫长的告白》,这部剧是什么性质的影视剧?这部剧是属于美学文艺性质的影视剧,...
遗爱寺,小古文? 遗爱寺,小古文?遗爱寺是白居易写的一首诗。描写了晚春时的情景。写出了年华的流逝。
什么的司马迁? 什么的司马迁?写史记的司马迁伟大的写史记的司马迁,字子长,陕西韩城人。西汉的司马迁;史官司马迁;史学...
推荐吉他 推荐吉他请推荐一款优秀的合板琴。本身不想花更多的钱买单板了。上一把坏掉的单板是红松面单的。感觉和千元...
德国一位古典哲学代表人物名 德国一位古典哲学代表人物名著有《黑格尔哲学批判》 《未来哲学原理》等书 名字是四个字的,第三个字是巴...
小孩如何练好字 小孩如何练好字小孩如何练好字坚持,从书写姿势开始,选择适合孩子每个年龄段的字帖练习,每一步都比较重要...
人体每天摄入的热量和消耗的热量... 人体每天摄入的热量和消耗的热量应该怎么计算?成年男性2000-2250大卡,正常情况下。成年女性18...
请列举战国七雄的名称 请列举战国七雄的名称秦 齐 楚 燕 韩 赵 魏