CLR 垃圾回收
2019-01-06 21:54:03 0 举报
AI智能生成
CLR 垃圾回收梳理
作者其他创作
大纲/内容
CLR 垃圾回收
手动管理内存容易出现的Bug
未释放内存导致内存泄漏
引用已释放的内存
托管堆创建对象的过程
CLR维护指针NextObjPtr,指向下一个对象分配空间的地址
new 操作符的过程
计算类型所需字节
加上两个对象开销字节
类型对象指针
同步块索引(用来GC)
CLR检查托管堆剩余空间,如果有足够空间就在NextObjPtr所指向的地址处放入对象
将NextObjPtr指向下一个对象放入的地址
new 操作符返回对象引用
垃圾回收算法
引用计数算法
过程
每个对象都维护一个内存来统计有多少“部分”正在使用对象,当计数为0时进行垃圾回收
缺点
当出现循环引用时,计数将永远不会为0
引用追踪法
只关心引用类型的变量,因为值类型的变量直接包含值类型的实例
引用类型的变量称为根
GC开始时,暂停所有线程
遍历托管堆中所有的对象,并将同步索引块中的一位设为0
检查所有的活动根,查看他们引用那些对象,并将这些对象同步索引块中的一位设为1
如果根包含null,及未引用任何对象,则忽略这个根,继续检查下一个,直到检查完毕
对标记为0的对象(不可达)进行垃圾回收,标记为1的对象(可达)不回收
进入GC压缩阶段,对标记为1的对象进行压缩,使他们占用连续的内存空间
CLR对每个根减去所引用对象在内存中偏移的字节数,以保证每个根还是引用之前的对象
将NextObjPtr指向最后一个幸存对象之后的位置
如果内存已满,则在使用new开辟新的内存空间时会引发OutOfMemoryException
优点
解决循环引用问题
GC压缩后,使对象仍占用连续的内存空间,解决堆空间的内存碎片化问题,减小应用程序工作集,提高性能
开发中注意事项
尽量避免使用静态字段
原因
静态字段引用会一直存在,直到用于加载类型的AppDomain被卸载为止
当静态字段引用一个集合对象,并不断的向集合对象中加入数据,则会引发内存溢出
使用“代”提升性能
几点假设
对象越新,生存期越短
对象越老,生存期越长
回收堆的一部分,速度快于回收整个堆
每一代都会都会有一个预算容量,如果超过这个预算容量就要对这一代对象进行一次垃圾回收
GC托管堆只支持3代对象
第0代对象
新构造的对象,垃圾回收器从未检查它们
当超过预算,则会对第0代对象进行压缩回收,幸存对象称为第1代对象
在垃圾回收时要检查老对象的引用更新
利用JIT编译器内部机制,当对象引用发生变化时,会设置一个位标志
垃圾回收器就知道哪些老对象的引用已写入
对字段发生变化的老对象进行检查是否引用了任何第0代的任何新对象
第1代对象
在0代对象回收中存活的对象称为第1代对象,它们经历过一次垃圾回收器的检查
当对第0代对象进行压缩而超过第1代对象预算时,则对第1代进行压缩回收,幸存对象称为第2代对象
第2代对象
在1代对象回收中存活的对象称为第2代对象,它们经历过二次垃圾回收器的检查
当没有回收到足够的内存时,GC会执行一次完整回收,还不够就是引发OutOfMemoryException
垃圾回收触发条件
非强制回收
当第0代对象超出预算时
CLR正在卸载AppDomain
CLR在正常终止时关闭
强制回收
代码显示调用System.GC的静态Collect方法
Windows报告低内存情况
垃圾回收模式
主要模式
工作站模式
针对客户端应用程序优化的GC
降低GC造成的延迟
服务器模式
并发回收
被优化的主要时吞吐量和资源利用
子模式
并发模式
GC会有一个额外的线程,在程序运行时并发标记对象,查找不可达对象。当GC开始时,不需要再进行标记,可直接进行压缩,减少GC时间
使用并发模式,应用程序消耗的内存比非并发的多
非并发模式
收藏
收藏
0 条评论
下一页