一、什么是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中的碎片问题。