如何使用 IDisposable 接口销毁非托管对象?

How to destroy an un-managed object using IDisposable Interface?

我知道如何在 class 中使用 IDisposable 界面。但是,我不知道如何释放由 class 变量分配的内存?例如,在我的 class 中,我创建了一个 List<Bitmap>,其中包含列表中的 500 个位图图像,我正在使用 dispose 方法清除此列表。但是,当我使用 GC.GetTotalMemory().

获取总内存时,它显示相同的结果

在这里,我创建了一个由 IDisposable 接口实现的示例 class。

public class disposable : IDisposable
{
    List<Bitmap> list = new List<Bitmap>();
    public disposable()
    {
        for (int i=0; i< 500; i++)
        {
            Bitmap bmp = new Bitmap(1024,768);                    
            using (Graphics g = Graphics.FromImage(bmp))
            {
                g.FillRectangle(Brushes.Red, new Rectangle(0,0,1024,768));
            }
            list.Add(bmp);                    
        }
    }


    public void Dispose()
    {
        list.Clear();
        list = null;
    }
}

然后我执行了下面的代码

static void Main(string[] args)
{

    long l1 = GC.GetTotalMemory(true);
    using (disposable d = new disposable())
    {
        //nothing to do
    }

    long l2 = GC.GetTotalMemory(false);

    Console.WriteLine(l1.ToString());
    Console.WriteLine(l2.ToString());

    Console.ReadKey();
}

输出说。

181764 //bytes before creating object of disposable class
222724 //bytes after creating object of disposable class

然后我尝试了没有 using 关键字的简单声明。

Dim d As New disposable();

但是,它给了我相同的结果。

所以,我的问题是为什么即使我处理了对象,那些位图图像分配的内存也没有释放。这没有任何区别。我正在清除列表中的所有项目,并将空值分配给 list 对象。

我也尝试过声明字节数组而不是 list<Bitmap>SqlConnection 对象。

但是,这两种方法(简单声明和 using 语句)之间分配的总内存没有任何差异。

long l1 = GC.GetTotalMemory(true);

//SqlConnection cnn = new SqlConnection("");    //1st Method

using (SqlConnection cnn = new SqlConnection("")) //2nd Method
{
    //nothing to do
}

long l2 = GC.GetTotalMemory(false);

Console.WriteLine(l1.ToString());
Console.WriteLine(l2.ToString());

我不明白为什么它不清除由托管和非托管对象分配的内存?

垃圾收集器不会一直 运行,只有在必要时才会这样做。在我的测试中,有时它可能 运行 仅在 5 分钟内出现一次。您可以强制垃圾收集器使用 GC.Collect();GC.WaitForPendingFinalizers();

进行收集

清除列表或为对象分配 null 值不会像 C/C++ free() 调用那样释放内存。它只是向 运行 时间指示您已完成该对象。 GC(垃圾收集器)会不时 运行。它将遍历对象并跟踪每个对象的所有权树。如果它发现对象没有所有者,它将释放内存(return 它到托管堆)。它实际上比那个复杂一点,根据大小和用途有多个 "generations",但想法是一样的。在对象为 "freed" 之前,对象终结器(如果已定义)为 运行。这个想法是系统不会花很多时间处理内存管理......除非它必须这样做。

.NET 和 Java 中的垃圾收集器的设计前提是

  1. 释放内存的目的是为了满足未来的分配请求;
  2. 在执行垃圾收集之前等待更长时间,到某个点,通常会增加它可以释放的内存量,而不是增加计算成本(从而降低平均每兆字节的净分配成本和填海)。

因此,.NET 和 Java 都不会在最后一个剩余引用被销毁时寻求回收内存,而是在有机会使用回收的内存时寻求回收内存。他们通常不会等到所有内存都被填满才这样做(由于缓存和其他因素,GC 花费大部分精力重复回收一个小池通常更好)但是在没有任何即时需要的情况下对于回收的内存,找出哪些内存可用通常没有什么好处。