CLR via C# 读书笔记-21.托管堆和垃圾回收

前言

近段时间工作需要用到了这块知识,遂加急补了一下基础,CLR中这一章节反复看了好多遍,得知一二,便记录下来,给自己做一个学习记录,也希望不对地方能够得到补充指点。

1,.托管代码和非托管代码的区别

2.托管堆是什么?

3.托管堆基础,托管堆分配资源

4.内存泄漏 内存溢出、内存损坏

5.C# new操作符分配资源

6.垃圾回收算法

7.代:提升性能

8.垃圾回收触发条件

9.大对象

10.使用需要特殊清理的类型

11.使用包装了本机资源的类型

 

一、托管代码和非托管代码的区别

托管代码:执行过程交由运行时管理的代码。IL(中间语言)有时也成为托管代码。CLR负责提取托管代码,利用JIT编译为机器代码,从而执行它。(CLR提供 自动内存管理、安全边界、类型安全)

非托管代码:在CLR外部,由操作系统直接执行的代码。(按自己的想法提供垃圾回收、类型检查、安全)例如:COM 组件、ActiveX 接口和 Win32 API 函数,数据库连接

二、托管堆是什么?.

托管堆:CLR要求所有对象都从托管堆中分配。进程初始化时,CLR划出一个地址空间区域作为托管堆。

(CLR还要维护一个指针,我们称它作NextObjPtr。该指针指向下一个对象在堆中的分配位置)

三、托管堆基础,托管堆分配资源

在面向对象中,每个类型都代表可供程序提供的一种资源,要使用这些资源,必须为代表资源的这些类型分配内存

1.调用IL指令newobj,为代表资源的类型分配内存(一般使用C#new操作符来完成)

2.访问类型的成员来使用资源(有必要可以重复)

3.摧毁资源的状态以进行清理

4.释放内存。垃圾回收器独自负责这一步

四、内存泄漏 内存溢出、内存损坏

内存泄漏:申请了一块内存,但不需要时一直不去删除,导致这块内存一直被占用

内存溢出:程序在申请内存时,没有足够的内存空间供其使用,抛出OutOfMemoryException

内存损坏:访问被释放的内存

五、C#的new操作符导致CLR执行以下步骤

1.计算类型的字段(以及从基类型继承的字段)所需的字节数

2.加上对象开销所需的字节数。每个对象都有两个开销字段:类型对象指针和同步块索引。

32位应用程序,这两个字段各自需要32位,所以每个对象要增加8字节

64位应用程序,这两个字段各自需要64位,所以每个对象要增加16字节

3.CLR检查区域中是否有分配对象所需的字节数。如果托管堆有足够的可用空间,就在NextObjPtr指针指向的地址处放入对象,为对象分配的字节会被清零。接着调用类型的构造器(为this参数传递NextObjPtr),new操作符返回对象引用。就在返回这个引用之前,NextObjPtr指针的值会加上对象占用的字节数来得到一个新值,及下个对象放入托管堆时的地址

六、垃圾回收算法

 1.引用计数算法。

Microsoft自己的“组件对象模型”(Component Object Model,COM),每个对象都维护着一个内存字段来统计程序中多少“部分”正在使用对象,随着每一“部分”到达代码中某个不再需要对象的地方,就递减对象的计数字段。计数对象变为0,对象就可以从内存中删除了。

许多引用技术系统最大的问题是处理不好循环引用。这种引用会阻止两个对象的计数器达到0,所以两个对象永远不会删除

2.引用跟踪算法

所有引用类型的变量都成为根。

CLR开始GC时,首先暂停进程中的所有线程,这样可以防止CLR检查期间访问对象并更改其状态。然后进入GC的标记阶段,将所有对象同步块索引的一位设为0,这表明所有对象都应被删除。任何根如果引用了堆上的对象,CLR就会将该对象的同步块索引中的位设为1,一个对象被标记后,CLR就会检查那个对象的根,标记他们的引用对象。如果发现对象已经标记,就不重新检查对象的字段。这样就避免了循环引用而产生死循环。

已标记的对象不能被垃圾回收。CLR知道哪些对象可以幸存,哪些对象可以删除后,会进入GC的压缩阶段,压缩所有幸存下来的对象,使它们占用连续的内存空间。减少了应用程序的工作集,从而提升将来访问这些对象时的性能。解决了本机堆空间碎片化的问题。

但是引用幸存对象的根现在引用的还是对象最初在内存中的位置,被暂停的线程恢复执行时,将访问旧的内存位置,会造成内存损坏,所以GC进入压缩阶段后,CLR还要从每个根减去所有引用的对象在内存中偏移的字节数。

压缩好内存后,NextObjPtr将指向最后一个幸存对象之后的位置,CLR恢复应用程序的所有线程。

注:静态字段引用的对象一直存在,直到用于加载类型的AppDomain卸载为止

有趣的三张图示留给读者思考:

图示一:

图示二:

图示三:

七、代:提升性能

CLR的GC是基于代的垃圾回收器,它对代码做了如下假设:

对象越新,生存期越短。

对象越老,生存期越长。

回收堆的一部分,速度快于回收整个堆。

托管堆在初始化时不包含对象。添加到堆的对象被称为第0代对象。CLR初始化时为第0代对象选择一个预存容量。如果分配一个新对象造成第0代超过预算,就必须启动一次垃圾回收。在一次垃圾回收后存活的第0代对象会成为第1代对象(事实上CLR还必须为第1代选择预算),第0代对象就不包含任何对象了。周而复始(第1代预算内存不满时不会自动释放内存),直到某一次,第0代和第1代预算内存都满了,第1代可能已经有许多对象变得不可达,这次垃圾回收器会检查第1代和第0代的所有对象,两代都被垃圾回收。和之前一样,第0代幸存者被提升为第1代,第1代幸存者被提升为第2代,第0代空出来了。

托管堆只支持三代:第0代、第1代和第2代。CLR初始化时,会为每次带选择预算。CLR的垃圾回收器是自调节的,会根据应用程序的内存负载自动优化。

八、垃圾回收触发条件

1.代码显示调用System.GC的静态Collect方法

2.Windows报告低内存情况

CLR使用Win32函数CreateMemoryResourceNotification和QueryMemoryResourceNotification监视系统的总体内存使用情况

3.CLR正在卸载AppDomain

CLR认为一切都不是根,执行涵盖所有代的垃圾回收

4.CLR正在关闭

整个进程都要终止了,Windows将回收进程的全部内存

九、大对象

CLR将对象分为大对象和小对象。

85000字节或更大的对象是大对象。

1.大对象不是在小对象的地址空间分配,而是在进程地址空间的其它地方分配

2.目前版本的CLR不压缩大对象,因为在内存中移动他们的代价过高

3.大对象总是第2代,绝不可能是第0代或第1代

十、使用需要特殊清理的类型

有的类型除了内存还需要本机资源(文件、网络连接、套接字、互斥体),GC回收托管堆中使用的内存,会造成本机资源的泄漏。

CLR提供了成为终结的机制,允许对象在被判定为垃圾之后,但在对象内存被回收之前执行一些代码。

终结基类System.Object定义了受保护的虚方法Finalize。垃圾回收器判定对象是垃圾后,会调用对象的Finalize方法(如果重写)

C#要求在类名前添加~符号来定义Finalize方法,如下图所示:

被视为垃圾的对象在垃圾回收完毕后才调用Finalize方法,这些对象的内存不是被马上回收的。

因为Finalize方法可能要执行访问字段的代码,可终结对象在回收时必须存活,造成被提升到另一代,使对象活的比正常时间长,增大了内存好用,所以应尽可能避免终结。

注:Finalize方法的执行时间是控制不了的。

十一、使用包装了本机资源的类型

如果想允许使用者控制类所包装的本机资源的生存期,就必须实现IDisposable接口。

如果类定义的一个字段的类型实现了dispose模式,那么类本身也实现了dispose模式。

”dispose一个对象”真正的意思是:清理或处置对象中包装的资源(比如它的字段引用的对象),然后等着在一次垃圾回收之后回收该对象占用的托管堆内存(此时才释放)

using语言编译器自动生成try和finally块,在finnally块中编译器生成代码将变量转型为一个IDisposable并调用dispose方法。所以using语句只能用于实现了IDisposable接口的类型。

一个清理资源的范例:

    class People:IDisposable
    {
        //这是一个Finalize方法
        ~People()
        {
            Dispose(false); 
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private bool disposed = false;
        public virtual void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)
                { 
                    // Free any other managed objects here.
                }
                // Free any unmanaged objects here.
            }
            disposed = true;
        }
    }
View Code

 

 

参考:http://www.cnblogs.com/luminji/archive/2011/03/29/1997812.html

天道酬勤,大道至简,坚持。

posted @ 2017-11-09 01:52  快跑啊兔兔  阅读(310)  评论(0编辑  收藏  举报