【问题标题】:Will disposable object clone cause memory leak in C#?一次性对象克隆会导致 C# 中的内存泄漏吗?
【发布时间】:2012-02-05 14:21:04
【问题描述】:

检查此代码:

.. class someclass : IDisposable{
    private Bitmap imageObject;
    public void ImageCrop(int X, int Y, int W, int H)
    {
        imageObject = imageObject.Clone(new Rectangle(X, Y, W, H), imageObject.PixelFormat);
    }
    public void Dispose()
    {
        imageObject.Dispose();
    }
}

Bitmap 在 C# 中是 ICloneableIDisposable

为了避免内存泄漏,对于 Disposable 对象,一般使用using,那么无论你的代码有多么错误,系统都会自动释放该对象。

在我的示例中,我不能使用 using,因为我不想处置该对象,我稍后需要它(整个类也会处置自己,因为它的 IDisposable 也是如此。

我的问题是:我有一个imageObject 对象,然后我使用它Clone() 方法克隆一个新对象并将其提供给旧对象变量。这是否会导致一个(克隆的或原始的)对象无处可去并且永远不会被释放,内存泄漏。

[编辑]

似乎大多数意见是Clone导致附加对象,旧的应该是Dispose()

这是新代码:

    public void ImageCrop(int X, int Y, int W, int H)
    {
            // We have 1 object: imageObject
            using (Bitmap croppedImage = imageObject.Clone(new Rectangle(X, Y, W, H), imageObject.PixelFormat))
            {
                    // We have 2 objects: imageObject and croppedImage
                    imageObject.Dispose(); // kill one, only croppedImage left
                    imageObject = new Bitmap(croppedImage); // create one, now 2 objects again
            } // the using() will kill the croppedImage after this
            // We have 1 object: imageObject
    }

而且应该是适当的处置资源。

【问题讨论】:

  • 为什么要复制两次?

标签: c# memory-leaks idisposable using icloneable


【解决方案1】:

using 只是在 finally 块中调用 Dispose
只要在所有代码路径中调用Disposesomewhere,就可以了。

如果您不调用Dispose,GC 将最终为您处理它,但这可能会导致资源争用。

在这种特殊情况下,您可能应该在克隆原件后将其丢弃,因为看起来您再也不会使用它了。

【讨论】:

    【解决方案2】:

    我不能肯定地说,但如果你害怕它可能,为什么不将图像克隆到一个新变量,处置原始,然后重新分配:

    public bool ImageCrop(int X, int Y, int W, int H)
    {     
        Bitmap croppedImage = imageObject.Clone(new Rectangle(X, Y, W, H), imageObject.PixelFormat);
        imageObject.Dispose();
        imageObject = new Bitmap(croppedImage);
        croppedImage.Dispose();
    }
    

    【讨论】:

      【解决方案3】:

      避免资源泄漏或过早处置错误的关键是确保每个 IDisposable 对象始终都是明确定义的负责处置它的所有者。有时,一个对象会公开一个方法,通过该方法它将承担传入对象的所有权。如果对象的所有者将其传递给此类方法,则对象的原始所有者应该处置它。否则,对象的所有者必须在销毁对象的最后一个引用之前处置该对象。

      如果someClass 拥有ImageObject,那么它可能应该在销毁对它的引用之前处置该对象。另一方面,如果一个对象持有对另一个对象的唯一引用,那么克隆持有的对象以重新分配原始引用似乎有点代码味道。我不知道最初是如何分配 ImageObject 的,但它似乎应该在您的对象中创建,或者基于传入的图像对象进行克隆。无论哪种情况,您都应该能够对传入图像的类型进行足够的控制,以选择无需(重新)克隆即可裁剪的类型。

      【讨论】:

        【解决方案4】:

        是的,这可能是泄漏。

        如果您要复制一次性对象(在您的情况下为位图),则应立即处理不再需要的实例。

        使用关键字只是手动进行 try-finally 和调用 Dispose() 的一种便利。如果在您的情况下您不能使用“使用”,那么只需使用 try-finally 块并确保清除悬空资源。

        【讨论】:

          【解决方案5】:

          托管代码中的内存不会泄漏,但可能会导致资源泄漏。位图是 Windows 中较低级别对象的包装器,它是一次性的,因此可以正确清理较低级别的对象。如果您不释放对象,通常应该在一段时间后由垃圾收集器处理它们,但不能保证它会真正被释放。

          克隆图像会创建一个新对象,该对象应自行处理。当原始图像被克隆替换时,您应该处理它。您可以为此使用 using 关键字:

          public bool ImageCrop(int X, int Y, int W, int H) {
            using (Bitmap original = imageObject) {
              imageObject = original.Clone(new Rectangle(X, Y, W, H), imageObject.PixelFormat);
            }
          }
          

          【讨论】:

          • 您认为您创建了一个新对象“原始”,稍后将其处理掉。这与原始对象和克隆对象无关?
          • @EricYin:是的,确实如此。原始对象被释放,克隆存储在属性中。
          【解决方案6】:

          假设 Clone() 正常工作,它将为您提供 2 个 Disposable 对象来管理。两者都需要 Disposed()。

          所以我认为它不会解决您的问题。

          方法返回 IDisposable 对象并不少见,您只需确保在更高级别进行(异常安全)资源管理。小心地这样做。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2010-10-03
            • 1970-01-01
            • 2021-03-23
            • 2021-09-25
            • 2014-12-08
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多