C#GC

  1. C#gc

根:类中定义的任何静态字段,方法的参数,局部变量(仅限引用类型变量)等都是根,另外cpu寄存器中的对象指针也是根 。根是在堆以外能够找到的各类入口点。程序员

对象可达与不可达:若是一个根引用了堆中的一个对象,则该对象为可达,不然是不可达。算法

垃圾回收的基本原理:回收分为两个阶段:标记和压缩,标记的过程就是判断对象是否可达的过程。当全部的根检查完毕后,堆中将包含可达(已标记)与不可达(未标记)对象。数据库

标记完成后,进入压缩阶段。在这个阶段中,垃圾回收器线性遍历堆,以寻找不可达对象的连续内存块。并把可达对象移动到这里以压缩堆。这个过程有点相似于磁盘空间的碎片整理。安全

 

不可达对象清除后,移动可达对象实现内存压缩。网络

压缩以后,指向这些对象的指针和cpu寄存器都会失效,垃圾回收器必须从新访问全部根,并修改他们来指向对象的新内存位置。这会形成显著的性能损失。这个损失也是托管堆的主要缺点。函数

 

垃圾回收算法-分代算法性能

代是CLR垃圾回收器采用的一种机制,他惟一的目的就是提高应用程序的性能。分代回收,速度显然快于回收整个堆。优化

CLR托管堆支持3代:0,1,2。第0代的空间约为256kb,第一代约为2m,第二代约为10m。新构造的对象会被分配到第0代。操作系统

 

当第0代空间满时,垃圾回收器启动回收,不可达对象会被回收,存活的对象会被归于第1代。指针

 

当第0代空间已满,第1代也开始有不少不可达对象以致空间将满时,这时两代垃圾都将被回收。存活下来的可达对象,第0代升为第1代,第1代升为第2代。

实际CLR的代回收机制更加智能,若是新建立的对象生存周期很短,第0代垃圾也会马上被垃圾回收器回收(不用等空间分配满)。另外,若是回收了第0代,发现还有不少对象可达,并无释放多少内存,就会增大第0代的预算至512kb,回收效果转变为:垃圾回收的次数将减小,但每次都会回收大量的内存。若是尚未释放多少内存,垃圾回收将执行彻底回收(3代),若是还不够,则会抛出内存异常。

也就是说,垃圾回收器会根据回收内存的大小,动态调整每一代的分配空间预算,达到自动优化。

 

.NET的GC机制有两个问题:

首先,GC并非能释放全部的资源,它不能自动释放非托管资源。

第二,GC并非实时性的,这将会形成系统性能上的不肯定性。因此有了IDisposable接口,它定义了Dispose方法,这个方法用来供程序员显式调用以释放非托管资源。使用using语句能够简化资源。

托管资源:托管资源指的是.NET能够自动进行回收的资源,主要是指托管堆上分配的内存资源。托管资源的回收工做是不须要人工干预的,有.NET运行库在合适调用垃圾回收器进行回收。

非托管资源:非托管资源指的是.NET不知道如何回收的资源,最多见的一类非托管资源是包装操做系统资源的对象,例如文件,窗口,网络链接,数据库链接,画刷,图标等。这类资源,垃圾回收器在清理的时候会调用Object.Finalize()方法。默认状况下,方法是空的,对于非托管对象,须要在此方法中编写回收非托管资源的类,能够将释放非托管资源的代码放在析构函数。

频繁调用GC.Collect()方法会下降程序的性能,当应用程序代码中某个肯定的点上使用的内存量大量减小时,在这种状况下使用 GC.Collect 方法可能比较合适。

 

.NET提供了三种释放方法:

       第一种:提供Close方法:

               关闭对象资源,在显示调用时被调用。Close方法是不存在的,是使用者本身写的,正常状况下Close方法里面会调用Dispose()方法。

       第二种:Dispose

               继承IDisposable接口,实现Dispose方法,调用Dispose方法,销毁对象,须要显示调用或者经过using语句,在显示调用或者离开using程序块时被调用。

Dispose方法用于清理对象封装的非托管资源,而不是释放对象的内存,对象的内存依然由垃圾回收器控制。

Dispose方法调用,不但释放该类的非托管资源,还释放了引用的类的非托管资源。

Dispose模式就是一种强制资源清理所要遵照的约定;Dispose模式实现了IDisposable接口,从而使得该类型提供一个公有的Dispose方法。

第三种:析构函数(Finalize)

        一个正常状况的类是不会写析构函数的,而一旦一个类写了析构函数,就意味着GC会在不肯定的时间调用该类的析构函数,判断该类的资源是否须要释放,而后调用finalize方法,若是重写了finalize方法则调用重写的finalize方法,Finalize方法的做用是保证.NET对象能在垃圾回收时清除非托管资源。

在.NET中Object.Finalize方法是没法重载的,编译器是根据类的析构函数来自动生成Object.Finalize方法的

        finalize由垃圾回收器调用;dispose由对象调用。finalize无需担忧由于没有调用finalize而使非托管资源得不到释放,但由于由垃圾回收器管理,不能保证当即释放非托管资源;而dispose一调用就释放非托管资源。

  GC.Collect(); //强制对全部代进行即时垃圾回收
当应用程序代码中某个肯定的点上使用的内存量大量减小时,在这种状况下使用 GC.Collect 方法可能比较合适。

实现模型: 
一、因为大多数的非托管资源都要求能够手动释放,因此,咱们应该专门为释放非托管资源公开一个方法。实现IDispose接口的Dispose方法是最好的模型,由于C#支持using语句快,能够在离开语句块时自动调用Dispose方法。 

二、虽然能够手动释放非托管资源,咱们仍然要在析构函数中释放非托管资源,这样才是安全的应用程序。不然若是由于程序员的疏忽忘记了手动释放非托管资源, 那么就会带来灾难性的后果。因此说在析构函数中释放非托管资源,是一种补救的措施,至少对于大多数类来讲是如此。 

三、因为析构函数的调用将致使GC对对象回收的效率下降,因此若是已经完成了析构函数该干的事情(例如释放非托管资源),就应当使用SuppressFinalize方法告诉GC不须要再执行某个对象的析构函数。 

四、析构函数中只能释放非托管资源而不能对任何托管的对象/资源进行操做。由于你没法预测析构函数的运行时机,因此,当析构函数被执行的时候,也许你进行操做的托管资源已经被释放了。这样将致使严重的后果。 

五、(这是一个规则)若是一个类拥有一个实现了IDispose接口类型的成员,并建立(注意是建立,而不是接收,必须是由类本身建立)它的实例对象,则 这个类也应该实现IDispose接口,并在Dispose方法中调用全部实现了IDispose接口的成员的Dispose方法。 
只有这样的才能保证全部实现了IDispose接口的类的对象的Dispose方法可以被调用到,确保能够手动释听任何须要释放的资源。

 

 

https://www.jianshu.com/p/26522934afdd

相关文章
相关标签/搜索