2010-11-05 110 views
2

我正在寫一個應用程序來讀取一個非常大的tif文件(15000x10000),然後將它切成256x256的拼貼圖,並保存爲jpeg。我正在嘗試使用WPF windows.media.imaging對象來做斬波。下面的例子運行正常,並會爲我抽取一些圖塊(它只是獲得同一個圖塊的多個副本),但是應用程序使用了內存,並且內存永遠不會被釋放。即使強制一個CG.Collect來測試,這仍然不能釋放內存。WPF圖像處理 - 內存永遠不會釋放

Dim croppedImage As CroppedBitmap 

Dim strImagePath As String = "C:\Huge.tif" 

    Dim imageSource As BitmapSource = TiffBitmapDecoder.Create(New Uri(strImagePath), BitmapCreateOptions.IgnoreImageCache, BitmapCacheOption.None).Frames(0) 'CreateImage(imageBytes, 0, 0) 
    Dim enc As JpegBitmapEncoder 
    Dim stream As FileStream 

    For i As Integer = 0 To 5000 
     croppedImage = New CroppedBitmap() 

     croppedImage.BeginInit() 
     croppedImage.Source = imageSource 
     croppedImage.SourceRect = New Int32Rect(0, 0, 256, 256) 
     croppedImage.EndInit() 

     enc = New JpegBitmapEncoder() 
     enc.QualityLevel = 70 
     enc.Frames.Add(BitmapFrame.Create(croppedImage)) 
     stream = New FileStream("C:\output\" & i & ".jpg", FileMode.Create, FileAccess.Write) 
     enc.Save(stream) 
     stream.Close() 

     enc = Nothing 
     stream = Nothing 
     croppedImage.Source = Nothing 
     croppedImage = Nothing 

    Next 

    imageSource = Nothing 

我在這裏丟失了什麼?我如何確保這些資源正確釋放?

感謝

更多信息:

低於一定幫助提供的答案。感謝那。我現在有另外一個問題需要補充。我想每個水印平鋪它是通過將下面的代碼保存之前:

Dim targetVisual = New DrawingVisual() 
Dim targetContext = targetVisual.RenderOpen() 

targetContext.DrawImage(croppedImage, New Rect(0, 0, tileWidth, tileHeight)) 
targetContext.DrawImage(watermarkSource, New Rect(0, 0, 256, 256)) 

Dim target = New RenderTargetBitmap(tileWidth, tileHeight, 96, 96, PixelFormats.[Default]) 

targetContext.Close() 
target.Render(targetVisual) 
Dim targetFrame = BitmapFrame.Create(target) 

這是開始使用一些嚴重的內存。通過任務管理器報告,運行大型tif時使用了超過1200MB的內存。看起來這個內存最終會被釋放,但是我略微擔心代碼中的某些內容不正確,並且無論如何阻止它首先消耗所有這些內存。也許這只是歸結於Franci所討論的問題?

安德魯

回答

0

你的大對象肯定是提升到第2代,在循環中的〜25000次對象分配後。 Gen 2對象只在完整的集合上收集,很少完成,因此您的對象可能會在內存中駐留一段時間。

您可以嘗試使用GC.Collect()強制完整收集。您也可以在強制完成分配之前使用其他GC方法檢查分配的內存,然後等待它完成,然後再次檢查分配的內存以確定是否確實收集了大型對象。

請注意,即使可能發生完全集合,內存已包含在進程工作集中,並且操作系統可能需要一點時間才能釋放該內存並將其從工作集中刪除。如果要通過查看「任務管理器」中設置的進程工作集來判斷內存是否正確收集,請務必記住這一點。

+0

感謝您的輸入。我想我需要更多地瞭解垃圾收集器以及它是如何工作的。 – 2010-11-08 10:33:54

0

我重新本地代碼,並做了一個小實驗,結果我發現,如果你做出stream當地的for循環,並使用enc.SaveUsing stream As New FileStream(System.IO.Path.Combine(outputDir, i.ToString() & ".jpg"), FileMode.Create, FileAccess.Write)總數專用字節在60 MB大約保持(對80+ MB不在using內使用)。

我供你參考完整代碼:

Dim fileName As String = System.IO.Path.Combine(Environment.CurrentDirectory, "Image1.tif") 
Dim outputDir As String = System.IO.Path.Combine(Environment.CurrentDirectory, "Out") 

Dim imageSource As BitmapSource = TiffBitmapDecoder.Create(New Uri(fileName), BitmapCreateOptions.IgnoreImageCache, BitmapCacheOption.None).Frames(0) 

Dim enc As JpegBitmapEncoder 
Dim croppedImage As CroppedBitmap 

For i As Integer = 0 To 4999 
    croppedImage = New CroppedBitmap() 

    croppedImage.BeginInit() 
    croppedImage.Source = imageSource 
    croppedImage.SourceRect = New Int32Rect(0, 0, 256, 256) 
    croppedImage.EndInit() 

    enc = New JpegBitmapEncoder() 
    enc.QualityLevel = 70 
    enc.Frames.Add(BitmapFrame.Create(croppedImage)) 
    Using stream As New FileStream(System.IO.Path.Combine(outputDir, i.ToString() & ".jpg"), FileMode.Create, FileAccess.Write) 
     enc.Save(stream) 
    End Using 

    enc = Nothing 
    croppedImage.Source = Nothing 

    croppedImage = Nothing 
Next 

imageSource = Nothing 
+0

這當然有助於控制內存使用情況。不過,當我試圖對水印進行水印處理時,我還有另一個問題。我已經將此添加到上面的問題。 – 2010-11-08 11:12:25

+0

使用(流...)只是調用stream.Dispose,它與流相同。關閉(除非拋出異常)。如果您發現內存使用情況有任何差異,那麼您測量它的方式有點不妥。 – adrianm 2010-11-08 11:57:44

+0

@adrianm我正在使用sysinternals進程資源管理器專用字節分配檢查內存分配。除此之外,我對enc.Save方法進行了調整,因爲在通過vs2010性能分析工具進行分析時報告使用最大內存。請讓我知道是否有更好的方法。謝謝。 – 2010-11-08 19:21:14

0

嘗試創建TiffBitmaDecoder像這樣:

Dim imageSource As BitmapSource = TiffBitmapDecoder.Create(New Uri(fileName), BitmapCreateOptions.IgnoreImageCache, BitmapCacheOption.OnLoad).Frames(0)