2016-03-23 73 views
0

由于上个月我对一个不太明显的非托管资源有一个非常讨厌的问题,所以对于内存泄漏问题我有一点点超调。 我只是编写了一个非常简单的测试应用程序,带有一个带有两个不同图片的按钮,并且注意到我不太确定这里是否存在“问题”或不...图像资源内存

如果我有2个图片资源PIC1和PIC2和ImageButton的对象时,它只是从用户控件和一个改变的OnPaint继承了一些对象:

protected override void OnPaint(PaintEventArgs e) 
{ 
    base.OnPaint(e); 
    //stuff 
    if (this.keyStatus)) 
    { this.imageButton.DefaultImage = Resource1.Pic1; } 
    else 
    { this.imageButton.DefaultImage = Resource1.Pic2; } 
    e.Graphics.DrawImage(this.defaultImage, this.ClientRectangle); 
} 

旁边的OnPaint不被分配DefaultImage(一个好地方它只是在这里告诉你我是什么意思在一小段代码中),我只是在这里分配一个对我的预编译资源的引用,是吗?如果我使用new Bitmap(Resource1.Pic1)来调用它,我不会创建副本。 因此,如果我每5秒更换一次keyStatus,我的屏幕上就会出现一个非常烦人的图片,并且有很多变化,但是没有任何问题,图片会随时发生变化或不可见,从而不时泄漏内存。正确?

非常感谢!

+2

资源设计人员在生成正确的代码方面做得不好。 'Pic1'和'Pic2'属性应该是方法。每使用一次该属性它们都会返回一个* new *位图,该位图应该在使用后丢弃。所以在这个非常可怕的borken代码片段中,如果不是null,你必须处理imageButton.DefaultImage。通常,没有100%的保证,这个图像不是来自其他地方,因此可能在其他地方使用。很难评论糟糕的代码。 –

+0

@HansPassant什么?好吧,谢谢你的提高,改变一切。我会改变我的答案; Zorakh似乎并没有明白你的意思。 – atlaste

+0

嗨,谢谢你的回答,虽然我不太清楚你的意思是Pic1和Pic2是返回一个新的位图实例的方法。我特别将它们作为资源包含在我的项目中,以便每次都使用相同(且唯一)的副本,而无需在途中处理实例。如果我的答案是正确的,这里就是这种情况,所以在这里我不必担心这一点(实际上我没有观察到内存问题)。 或者在我上面的示例中是这样的,但是最好是像你说的那样去做?在那种情况下:你能告诉我优点吗? Thx! – Zorakh

回答

0

对象引用是如何工作的

假设你有一个随机的对象。该对象是类类型(不是值类型),而不是IDisposable。基本上这意味着以下内容:

// y is some object 
var x = y; 

现在,x不会复制y中的所有数据,只是简单地对y的内容进行新的引用。简单。

为确保不会发生内存泄漏,GC会跟踪所有对象并(定期)检查哪些对象可以访问。如果一个对象仍然可以访问,它不会被删除 - 如果不是,它将被删除。

然后有非托管代码

只要你坚持托管代码,一切都很好。当你遇到非托管代码(比如说:GDI +,它是许多System.Drawing内容的本地对象)时,你需要做额外的记录来摆脱代码。毕竟,.NET运行时不太了解非托管数据 - 它只知道有一个指针。因此,GC将清除指针,但不清除数据 - 这会导致内存泄漏。

因此,来自.NET的家伙增加了IDisposable。通过实现IDisposable,您可以实现额外(非托管)清理,例如释放非托管内存,关闭文件,关闭套接字等。

现在,GC知道终结器,它是作为Disposable模式的一部分实现的:https://msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.110).aspx)。但是,您通常不希望等待GC运行来清理非托管资源。因此,当一个对象可以被清理并且具有非托管资源时,通常呼叫Dispose()是个好主意。

System.Drawing.Bitmap一样,它实现了IDisposable

在大多数情况下,您可以简单地将IDisposable包装在using语句中,该语句将在不错的try/finally子句中为您调用Dispose()。例如:

using (var myBitmap = new Bitmap(...)) 
{ 
    // use myBitmap 
} 
// myBitmap including all resources are gone. 

什么资源位图

@HansPassant指出,资源位图生成每次你访问一个位图属性时新Bitmap。这基本上意味着位图被复制并需要处理。

换句话说:

// Free the old bitmap if it exists: 
if (this.imageButton.DefaultImage != null) 
{ 
    this.imageButton.DefaultImage.Dispose(); 
    this.imageButton.DefaultImage = null; 
} 

// assign new imageButton.DefaultImage 

所以,这解决了内存泄漏,但会给你很多是围绕复制的数据。

如果你不想处理

这里谈到为什么我被从汉斯此言惊讶的部分:)基本上,你每次分配Bitmap到一个按钮,让你不想一遍又一遍地复制数据 - 这没什么意义。

因此,你可能会得到的想法资源包装成一个“静态”的容器和根本不释放它在所有:

static Bitmap myPic1 = Resource1.Pic1; 
static Bitmap myPic2 = Resource1.Pic2; 

... 

if (this.keyStatus)) 
{ 
    this.imageButton.DefaultImage = myPic1; 
} 
else 
{ 
    this.imageButton.DefaultImage = myPic2; 
} 

这工作,但会给你,如果你在某个问题点决定也生成图像。举例说,我们改变这样的代码:

if (this.keyStatus)) 
{ 
    this.imageButton.DefaultImage = myPic1; // #1 don't dispose 
} 
else 
{ 
    Bitmap myPic3 = CreateFancyBitmap(); // #2 do dispose 
    this.imageButton.DefaultImage = myPic3; 
} 

现在,这里的问题是与组合。 myPic1是一个静态对象,不应该丢弃。另一方面,myPic3不是,应该处置。如果你打电话Dispose(),你会在#1处得到一个令人讨厌的异常,因为数据不再存在。没有适当的方法来区分这两者。

+0

你好,非常感谢,这就是为什么我问,有很多IDisposables,但在这个非常简单的情况下,我并不觉得我真的需要处理一些东西,因为我只是传递这个参考。 Dou你得到Hans Passant在评论Pic1和Pic2时意味着返回新位图的方法吗?很明显,我需要在这种情况下进行处理,但我们是不是认为这不是这种情况? – Zorakh

+0

@Zorakh我假设我的更新答案回答你的问题? – atlaste