【问题标题】:How to destroy an un-managed object using IDisposable Interface?如何使用 IDisposable 接口销毁非托管对象?
【发布时间】:2023-03-10 16:04:01
【问题描述】:

我知道如何在课堂上使用IDisposable 接口。但是,我不知道如何释放由我的类变量分配的内存?例如,在我的课堂上,我创建了一个List<Bitmap>,列表中有 500 个位图图像,我正在 dispose 方法中清除此列表。但是,当我使用GC.GetTotalMemory() 获取总内存时,它显示相同的结果。

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

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&lt;Bitmap&gt;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());

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

【问题讨论】:

标签: c# memory-management idisposable


【解决方案1】:

垃圾收集器并非一直运行,而是仅在必要时运行。在我的测试中,有时它可能在 5 分钟内只运行一次。您可以使用GC.Collect();GC.WaitForPendingFinalizers(); 强制垃圾收集器进行收集

【讨论】:

  • 那么这是否意味着将null 值分配给对象引用不会直接从堆存储中清除内存?我只是从对象引用中删除地址?
  • 不,它不会立即清除内存。它最终会被清理,但不会马上清理。这就是为什么 Dispose 与终结器相反存在的原因。 Dispose 方法将在对象超出范围时执行,即使用 using 子句,因此您可以清理我们正在使用的资源。当 GC 运行时,finalizer 将在更晚执行。
  • 感谢 Zdravko Danev,您的回答对我很有帮助。我学到了一些关于 IDisposable 的新知识。之前,这个答案对我来说 IDisposable 接口的定义是不同的。
  • @Shell:急切释放内存的系统这样做是为了告诉内存管理器“这里有一些内存,你下次需要时可以使用——你可以跟踪它”。如果内存直到可以分配给其他东西的确切时刻才被释放,内存管理器可能会简单得多,但当然事情很少会这样。 GC 背后的一个想法是,应该在不再需要它的点和它可以用于其他用途的点之间的某个时间回收内存,但在该间隔内何时发生回收并不重要。
【解决方案2】:

清除列表或为对象分配 null 值不会像 C/C++ free() 调用那样释放内存。它只是向运行时指示您已完成该对象。 GC(垃圾收集器)会不时运行。它将查看对象并跟踪每个对象的所有权树。如果它发现对象没有所有者,它将释放内存(将其返回到托管堆)。它实际上比这要复杂一些,并且根据大小和使用情况有多个“世代”,但想法是一样的。在对象被“释放”之前,对象终结器(如果已定义)运行。这个想法是系统不会花费大量时间处理内存管理......除非它必须这样做。

【讨论】:

  • 感谢@Dweeberly,您也以某种方式消除了我的疑问。我以错误的方式测试我的代码。你能再帮我一点吗。你能给我一个文章或帖子的链接,它可以帮助我决定何时应该调用 GC.Collect() 方法吗?
  • 我不确定有没有。在大多数情况下,您不想手动调用 GC.Collect。与往常一样,该规则也有例外。你可能想看看这个 SO 问题 stackoverflow.com/questions/478167/… 和这个 MS 博客 blogs.msdn.com/b/ricom/archive/2004/11/29/271829.aspx
【解决方案3】:

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-14
    • 2013-07-26
    • 1970-01-01
    • 1970-01-01
    • 2014-02-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多