2016-03-23 109 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