C# 关于位图、图像和`使用`块

C# 关于位图、图像和`使用`块,c#,.net,C#,.net,我陷入了这个问题 我觉得我的理解有问题。请澄清这些事情 Destructor&IDisposable.Dispose是释放不受.NET控制的资源的两种方法。也就是说,除了记忆以外的一切。对吧? 使用块是调用对象的IDisposable.Dispose()方法的更好方法 这是我指的主要代码。 class someclass { static someMethod(Bitmap img) { Bitmap bmp = new Bitmap(img); //state

我陷入了这个问题

我觉得我的理解有问题。请澄清这些事情

  • Destructor
    &
    IDisposable.Dispose
    是释放不受.NET控制的资源的两种方法。也就是说,除了记忆以外的一切。对吧?
  • 使用
    块是调用对象的
    IDisposable.Dispose()
    方法的更好方法
  • 这是我指的主要代码。

    class someclass
    {
        static someMethod(Bitmap img)
        {
            Bitmap bmp = new Bitmap(img); //statement1
            // some code here and return
        }
    }
    
    下面是我用于测试的类:

    class someotherClass
    {
        public static voide Main()
        {
            foreach (string imagePath in imagePathsArray)
            {
                using (Bitmap img1 = new Bitmap(imagePath))
                {
                    someclass.someMethod(img1);
                    // does some more processing on `img1`
                }
            }
        }
    }
    
    语句1是否存在内存泄漏

    问题1:如果每个图像大小都是10MB。那么这个
    bmp
    对象是否至少占用10MB的空间?我的意思是,它会复制整个图像吗?还是只是参考一下

    问题2:我应该还是不应该使用
    块将语句1放入

    我的论点是:我们不应该。因为
    使用
    不是为了释放内存,而是为了释放资源(本例中为文件句柄)。 如果我在
    使用
    块中使用它。它关闭由该
    bmp
    对象封装的文件句柄。这意味着我们也在关闭文件句柄 用于调用方的img1对象。哪个不正确

    由于内存泄漏。不,这里没有内存泄漏的范围。因为当返回此方法时,引用
    bmp
    将被销毁。 这使得它引用的内存没有任何指针。所以,它的垃圾被收集起来了。我说得对吗

    编辑:

    class someclass
    {
        static Bitmap someMethod(Bitmap img)
        {
           Bitmap bmp = new Bitmap(img); //can I use `using` block on this enclosing `return bmp`; ???
            // do some processing on bmp here
           return bmp;
        }
    }
    

    您将内存等同于托管资源,这不一定是同一回事

    如果您创建的新对象的类型实现了
    IDisposable
    ,则应在使用完该对象后对其进行处置。句号

    事实是,除非您使用类似Reflector的东西分解
    Bitmap
    类,否则您将不知道它如何处理在其构造函数中传递的另一个实例,或者它的
    Dispose
    方法实际上做了什么。包含
    IDisposable
    接口应被视为类开发人员在完成
    Dispose
    方法时发出的调用该方法的指令。您前面的问题说您正在观察内存泄漏;我们已经指出,您并不是在处理所有内容—您是否尝试过添加我们建议的dispose调用,并查看内存泄漏问题是否得到解决

    Destructor&IDisposable.Dispose是释放不可用资源的两种方法 在.NET的控制下。也就是说,除了记忆以外的一切。对吧?

    是的,你说得对

    使用块只是调用对象的IDisposable.Dispose()方法的更好方法

    它们不是一种“更好”的方式。只是有时候更方便

    语句1是否存在内存泄漏

    我不这么认为,虽然我不确定。如果位图包含终结器,那么它最终将被调用

    附言。
    我认为这篇文章可以帮助您。

    内存泄漏在.NET中非常罕见。您遇到的是资源泄漏(GDI)。从技术上讲,这些资源是“内存”,但它们来自一个特殊的保留内存池,它比托管对象堆(存储大多数对象数据的地方)小得多,.NET垃圾收集器不管理这个特定内存

    不在创建位图的同一例程中处理位图不会自动导致资源泄漏。事实上,由于类有一个终结器,它有可能在合理的时间内被清除;问题是,你不知道什么时候,如果你创建了太多的
    Bitmap
    对象而没有清理它们(使用
    Dispose
    ),你就有可能耗尽操作系统的GDI资源,最终导致系统范围内的奇怪行为,比如无法恢复最小化的窗口或屏幕上没有正确显示文本

    当您使用
    IDisposable
    对象时,您需要确保的是,当您处理完这些对象后,您最终会处理它们。以下代码没有问题:

    Bitmap CreateBitmap(...)
    {
        Bitmap bmp = new Bitmap(400, 400);
        // Do something with the bitmap
        bmp.SetPixel(1, Color.Red);
        // etc.
        return bmp;
    }
    
    此方法的方法名和返回类型都清楚地表明它正在分配但没有释放资源。它正在创建一个保存非托管资源的对象,由调用方来清理。只要您以这种方式使用该方法:

    using (Bitmap bmp = CreateBitmap(...))
    {
        // Do something else with the bitmap
    }
    
    …那你就没事了

    语义错误是创建
    位图
    ,只是让引用超出范围:

    public int PossiblyLeakyMethod()
    {
        Bitmap bmp = CreateBitmap(...);
        return bmp.Height;
    }
    
    这很糟糕。现在不再有任何东西引用
    位图了。垃圾收集器可能会很快决定完成它,因为它的寿命不够长,无法从Gen0升级。但它在这里仍然具有堆语义,这意味着当它超出范围时(即,当方法完成执行时),它不会最终确定

    正如我在其他答案中提到的,当您使用
    IDisposable
    时,您也不应该假设任何关于对象私有实现的内容。大多数核心框架
    IDisposable
    类都会有终结器,但其他人的库可能有一个没有终结器的坏实现,在这种情况下,上面的“可能”泄漏方法将保证泄漏

    即使存在终结器,也完全可以以GC无法处理的速度创建对象:

    public void Crash()
    {
        while (true)
        {
            Bitmap bmp = new Bitmap(1920, 1200);
        }
    }
    
    保持此操作运行,然后查看获取
    OutOfMemoryException
    所需的时间。
    Bitmap
    在这里有一个终结器并不重要;问题是,您正在耗尽的“内存”与GC正在管理的内存不同,并且终结器的运行速度不够快,无法释放所有非托管资源。GC仅在托管内存耗尽时启动

    最后一点:这也是一个错误
    public Bitmap CreateUselessBitmap()
    {
        using (Bitmap bmp = new Bitmap(400, 400))
        {
            // Do something with bitmap
            return bmp;
        }
    }