C#基础:GC机制中如何判断一个对象是否扔在被使用

一、前言

这个问题涉及了GC的内部算法和机制,了解它的机制对程序员编写代码具有指导意义。对于这些算法,应该立即并且能够口述他们的基本原理。

在.NET中,引用类型对象实例通常通过引用来访问,而GC判断堆中的对象是否仍然在被使用的依据也是引用。简单来说:当没有任何引用指向堆中的某个对象实例时,这个对象就被视为不再使用。

了解了判断对象是否被使用的依据后,来看一下GC查看引用的算法。垃圾回收机制把引用分为以下两类:

  • 根引用:往往指那些静态字段的引用。或者存活的局部变量的引用。
  • 非根引用:指那些不属于根引用的引用,往往是对象实例中的字段。

垃圾回收时,GC从所有扔在被使用的根引用出发,遍历所有的对象实例,那些不能被遍历到的对象将被视为不再被使用而进行回收。为了更加直观地理解根引用和非根引用,下面的代码给出了一个实际的例子。

using System;

namespace usingDemo
{
    public class Root
    {
        public static Employee staticEmployee;
        static void Main(string[] args)
        {
            // 静态变量
            staticEmployee = new Employee();
            // 局部变量
            Employee a = new Employee();
            // 局部变量
            Employee b = new Employee();
            // 实例成员
            a._boss = new Employee();

            Console.Read();
            Console.WriteLine(a);
        }
    }

    public class Employee
    {
        public Employee _boss;

        public override string ToString()
        {
            if(_boss == null)
            {
                return "没有BOSS";
            }
            else
            {
                return "有一个BOSS";
            }
        }
    }
}

上面的代码中,一共拥有两个局部变量和一个静态变量,这些引用都是根引用。而其中一个局部变量a拥有一个成员实例对象,这个引用就是一个非根引用。下图展示了执行到Console.Read();这行代码时运行垃圾回收时的情况。

如上图所示,在代码中执行到Console.Read()时,存活的根引用有staticEmployee和a,前者因为它是一个公共静态变量,而后者是因为后续代码仍然使用a。通过这两个存活的根引用,GC会找到一个非根引用staticEmployee._boss,并且发现三个仍然存活的对象。而b的对象则将被视为不再使用而被释放。

这里GC侦测出b引用不再被使用从而释放了b的对象,更简单地确保b对象被视为不再被使用的方法是把b引用置为null,即b=null。

另外,当一个从根引用出发的遍历抵达一个已经被视为在使用的对象时,将结束这一个分支的遍历,这样做是为了避免死循环。

二、总结

GC通过在使用的根引用遍历所有引用的对象实例。当一个对象不能被遍历时,将被视为不再被使用。

猜你喜欢

转载自www.cnblogs.com/dotnet261010/p/12331729.html