您的位置:

C#垃圾回收机制详解

一、什么是C#垃圾回收机制?

1、C#垃圾回收机制是一种自动管理内存分配和释放的技术。

2、开发者无需手动分配和释放内存,垃圾回收机制会自动检测和回收未使用的内存。

3、.Net Framework中的垃圾回收是通过CLR(Common Language Runtime)实现的。

二、C#垃圾回收机制的工作原理是什么?

1、垃圾回收器会定期扫描堆中的对象,判断哪些对象可以被释放。

2、垃圾回收器会维护一个对象使用计数器,当对象被引用时计数器加1,当引用被清除时计数器减1。

3、当对象的使用计数器为0时,说明该对象已经不再被引用,垃圾回收器会回收该对象占用的内存空间。

4、垃圾回收机制最大的优势在于它可以处理内存泄漏的问题,同时也可以避免空指针异常的发生。

三、垃圾回收器是如何判断哪些对象需要被回收的?

1、引用计数:通过维护对象的引用计数器来判断哪些对象可以被回收。但是,这种方法会存在循环引用的问题,即两个或多个对象相互引用。

class MyClass
{
    public MyClass Next { get; set; }
}

...

var a = new MyClass();
var b = new MyClass();
a.Next = b;
b.Next = a;

上述代码中,对象a和对象b的引用计数器都为1,但是它们相互引用,因此无法被回收。

2、可达性分析:通过在根对象中开始扫描堆中所有对象的引用情况,找到所有被引用的对象,将它们打上标记,没有被打标记的对象就是不可达的,可以被垃圾回收器回收掉。

class MyClass
{
    public MyClass Next { get; set; }
}

...

var a = new MyClass();
var b = new MyClass();
a.Next = b;

上述代码中,对象b没有被任何其它对象引用,因此可以被垃圾回收机制回收。

四、如何手动触发垃圾回收器?

1、System.GC.Collect()方法可以手动触发垃圾回收机制。

class MyClass
{
    ~MyClass()
    {
        Console.WriteLine("Destructor is called.");
    }
}

...

var a = new MyClass();
var b = new MyClass();
a = null;
b = null;
System.GC.Collect();

上述代码中,在调用System.GC.Collect()方法之前,对象a和对象b已经没有任何引用,它们可以被垃圾回收机制回收掉。在System.GC.Collect()方法执行后,垃圾回收机制会释放它们所占用的内存空间,并调用它们的析构函数。

2、System.GC.WaitForPendingFinalizers()方法可以保证所有的析构函数都被执行完之后再退出程序。

class MyClass
{
    ~MyClass()
    {
        Console.WriteLine("Destructor is called.");
    }
}

...

var a = new MyClass();
var b = new MyClass();
a = null;
b = null;
System.GC.Collect();
System.GC.WaitForPendingFinalizers();

上述代码中,在调用System.GC.WaitForPendingFinalizers()方法之前,垃圾回收机制并不会立即执行对象的析构函数。而是在进入程序的关闭阶段之前,保证所有的析构函数都被执行完。

五、如何处理大对象?

1、.Net Framework会将大对象(大于85,000字节)从堆中分配到一个单独的物理区域称为LOH(Large Object Heap)。

2、对于大对象的回收,垃圾回收器会单独运行“大对象压缩”和“大对象分离”两种策略。

3、“大对象压缩”是指将LOH中的对象进行碎片整理,以便后续分配大对象时不至于出现内存碎片的情况。

4、“大对象分离”是指将大对象分为更小的对象,并将它们分配到堆中的不同区域。这样可以避免不必要的内存碎片,并提高大对象的分配速度。

六、垃圾回收器会带来哪些问题?

1、性能问题:垃圾回收机制会消耗一些额外的CPU时间和内存资源。

2、非确定性问题:由于垃圾回收机制是非确定性的,因此开发者无法精确地控制内存分配和释放的时间点,可能会影响程序的响应速度。

3、无法处理非托管资源:垃圾回收机制只能处理托管对象,无法直接回收非托管资源(如文件句柄、数据库连接等),因此开发者需要手动释放这些资源。

4、垃圾回收器可能导致程序的暂停,这对于某些需要实时性的应用程序(如游戏、实时监测等)是不可接受的。

七、如何优化垃圾回收机制的性能?

1、避免频繁的内存分配和释放:频繁的内存分配和释放会导致垃圾回收机制频繁运行,降低程序的性能。

2、使用对象池:对象池可以缓存一些长时间不会被释放的对象,减少内存分配和释放的次数,提高程序的性能。

class MyClass
{
    //定义对象池
    private static ObjectPool
    pool = new ObjectPool
    (() => new MyClass());

    //从对象池中获取对象
    public static MyClass GetInstance()
    {
        return pool.GetObject();
    }

    //将对象放回对象池
    public void Release()
    {
        pool.PutObject(this);
    }
}

//定义对象池的实现
public class ObjectPool
      where T : new()
{
    private ConcurrentQueue
       objects;
    private Func
       
        objectGenerator; public ObjectPool(Func
        
         objectGenerator) { this.objectGenerator = objectGenerator ?? throw new ArgumentNullException("Generator is null."); this.objects = new ConcurrentQueue
         
          (); } public T GetObject() { if (this.objects.TryDequeue(out T item)) { return item; } else { return this.objectGenerator(); } } public void PutObject(T item) { this.objects.Enqueue(item); } }
         
        
       
      
     
    
   

3、手动回收大对象:

class MyClass
{
    byte[] buffer = new byte[1024 * 1024 * 10];//10MB的大对象

    //手动释放大对象
    ~MyClass()
    {
        if (buffer != null)
        {
            System.Runtime.InteropServices.Marshal.FreeHGlobal(new IntPtr(buffer));
            buffer = null;
        }
    }
}

上述代码中,当MyClass对象即将被垃圾回收机制回收时,它的析构函数会释放buffer所占用的内存空间,避免了大对象在LOH中的碎片问题。