.NET对象生命周期(垃圾回收)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/m0_37137902/article/details/80701513

在.NET中,定义了一个类后,就可以使用C#的new关键字分配任意数量的对象,new关键字返回的是一个指向堆上对象的引用,并不是真正对象本身。如果在方法作用域中将引用变量声明为本地变量,这个引用变量保存在栈内,以供应用程序以后使用。当想调用对象中的成员时,可以使用点操作符:。

结构是指值类型,直接分配在栈上,而从来不会放在.NET托管堆上。只有在创建类的实例时,才会产生堆的分配。

使用new关键字讲一个类实例分配在托管堆上,然后就不用管。

一个对象从代码库的任何部分都不可访问时,垃圾回收器会从堆中删除它。当然删除不一定立即执行,当CLR进行下一次垃圾回收时,对象才会被安全销毁。

CIL的new指令

当C#编译器遇到new关键字时,它会在方法中加入一条CIL newobj指令。.NET垃圾回收器是堆的“清洁工”,它会压缩空的内存块来实现优化,托管堆中保存指针,精确表示指示下一个对象将被分配的位置。

newobj指令会通知CLR执行下面的核心任务:

   1计算非配对象所需要的总内存数(包括数据成员和基类所需的内存)。

   2检查托管堆,确保有足够的空间来防止要分配的对象。如果空间足够,调用类型的构造函数,最终将内存中心对象的引用返回给调用者,它的地址恰好是下一个对象的指针的上一个位置。

   3最后在将引用返回给调用者之前,移动下一个对象的指针,指向托管堆上的下一个可用的位置。

应用程序中会频繁地分配对象,直到托管堆上的空间用完,这是当处理newobj指令时,如果CLR判定托管堆没有足够的空间来分配所请求的类型,就会执行一个垃圾回收来释放内存。

对象引用设置为空

如果我们将对象引用赋值为null,编译器会生成CIL代码来确保引用不再指向任何对象,将引用赋值为null并不是意味着强制垃圾回收器立即启动并把对象从堆上移除,只是显示的取消引用和之前所指对象的连接。

应用程序根的作用

简单地说root就是一个存储位置,保存着对象托管堆上的一个对象的引用。根可以属于以下任何一个类别:

     1全局对象的引用(C#中不允许,CIL代码中有全局分配对象,C#之于CIL就好比C++之于汇编语言)。

     2静态对象、静态字段的引用。

     3应用程序代码库中局部对象的引用。

     4传递进一个方法的对象参数的引用

     5等待被和总结对象的引用

     6任何引用对象的CPU寄存器

CLR会建立一个对象图,代表堆上每一个对象(对象图用来说明所有可访问对象),垃圾回收器从来不会在对象图中让一个对象出现两次,这样就避免了COM编程中的循环引用计数(circular refence count)


建立对象图来确定哪些对象不能由应用程序访问

一个对象呗标记为终结后(这里是C和F),它们就会从内存中清除。垃圾回收器使用了两个不同的堆,一个专门用来存储非常大的对象。这个堆在回收周期中较少顾及,因为要重新定位大对象的性能开销很大,尽管如此,认为“托管堆”是一个内存区域一般并没有什么问题。

对象的代

对于垃圾回收器来说,“代”的思路即是:对象在堆上存在的时间越长,它就更应该被保留。

        第0代:从没有被标记为回收的新分配的对象。

        第1代:在上一次垃圾回收中没有被回收的对象(已被标记回收,但获取了足够堆控件而未被删除)

        第2代:在一次以上的垃圾回收后仍然没有被回收的对象。

第0代个第1代被称为暂时代。垃圾回收器首先调查所有0代对象。如果标记和清除这些对象得到了所需数量的空间内存,任何没有被回收的对象都被提升到第1代。如果算上所有0代对象后,仍然需要更多内存,就会检查1代对象的“可访问性”并相应地进行回收。没有被回收的1代对象被提升到2代,若仍然需要更多地内存,就会检查2代对象的“可访问性”,如果第2代对象在垃圾回收后仍然存在,则仍然为2代对象,因为其实预定义的对象代的上限。

.NET1.0 至 .NET3.5的并发垃圾回收

在.NET4.0之前,运行时使用并发垃圾回收技术来清理不再使用的对象,在此模型下,当堆0代或1代对象执行回收时,垃圾收集器会暂时挂起当前进程中的所有活动线程,以确保应用程序在回收过程中不会访问托管堆。

.NET4.0及后续版本

.NET4.0开始,改变了垃圾回收器处理线程挂起的方式,它在清理托管堆上的对象时,实用后台垃圾回收,但并不是所有的垃圾回收都发生在额外的后台执行线程。后台垃圾回收用于非暂时代上的对象,而暂时代上的对象,.NET运行时将使用一个专用后台线程进行回收 

System.GC类

mscorlib.dll程序集提供了名为System.GC的类类型,它可以使用一些静态成员与垃圾回收器交互。一般情况下,只有在创建那些使用非托管资源的类时,才需要使用System.GC的成员。例如使用.NET平台调用协议基于C的Windows API,或一些非常低级且复杂的COM互操作逻辑。


                                                                                   部分System.GC成员

如下代码

  class Program
    {
        static void Main(string[] args)
        {
            Console .WriteLine ("**** Fun with System.GC ****");
            //输出对上估计的字节变量
            Console.WriteLine("Estimated bytes on heap:{0}",GC.GetTotalMemory (false ));

            //MaxGeneration是由0开始的,因此为了显示墓地加上了1
            Console.WriteLine("This OS has {0} object generations.\n", (GC.MaxGeneration + 1));
            Car refToMyCar = new Car("Zippy", 100);
            Console.WriteLine(refToMyCar.ToString());

            //输出refToMyCar对象的代
            Console.WriteLine("Generation of refToMyCar is :{0}", GC.GetGeneration(refToMyCar));
            Console.ReadKey();
        }

        public class Car
        {
            public int CurrentSpeed { get; set; }
            public string PetName { get; set; }
            public Car() { }
            public Car (string name,int speed)
            {
                PetName = name;
                CurrentSpeed = speed;
            }
            public override string ToString()
            {
                return string.Format("{0} is going {1} MPH", PetName, CurrentSpeed);
            }
        }
    }

                                                                 

                                                                           输出结果

强制垃圾回收

 用的比较少,主要应用于两中场景:

        1应用程序将要进入一段代码,后者不希望被可能的垃圾回收中断。

        2应用程序刚刚分配非常多的对象,希望尽可能的删除已获取的内存。

手动强制垃圾回收代码示例。

        //强制一次垃圾回收,并等待每一个对象都被终结
            GC.Collect();
            GC.WaitForPendingFinalizers();


猜你喜欢

转载自blog.csdn.net/m0_37137902/article/details/80701513