Memory Leak when Updating WPF Canvas Background

两盒软妹~` 提交于 2020-05-01 05:58:35

问题


I'm sure the solution to this is simple but I'm left scratching my head so maybe someone can shed some light here.

What I am doing is when my program starts I load an input file into a private variable (_bmpSource) and display it in a Canvas by creating an ImageBrush and setting the canvas's background. On a button click I modify the image by rotating it, and then display it by again setting the canvas's background property. It appears that I am mismanaging memory because the rotate/display operation works correctly for a few iterations but will eventually bomb complaining about insufficient memory.

NOTE: The image I'm using is a 5000x5000 pixel bmp.

I've tried everything from breaking references to calling garbage collection but it appears the canvas is still holding onto this data somehow.

Here is some sample code that I can reproduce the issue with.

Private variable declarations:

Private _SourceFileName As String = "C:\Users\sean\Desktop\Input.bmp"
Private _bmpSource As BitmapSource

This gets called in my constructor for the initial load/display of the image:

Me._bmpSource = Me.LoadImage()
Me._Canvas.Background = New System.Windows.Media.ImageBrush(Me._bmpSource)

LoadImage() implementation:

Private Function LoadImage() As BitmapImage

    Dim bmpImage As New BitmapImage
    Dim imageSourceStream As New FileStream(Me._SourceFileName, FileMode.Open, FileAccess.Read, FileShare.Read)
    Dim decoder As New BmpBitmapDecoder(imageSourceStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad)
    Dim encoder As New JpegBitmapEncoder

    encoder.Frames.Add(BitmapFrame.Create(decoder.Frames(0)))

    Dim streamSourcePath As String = My.Computer.FileSystem.GetTempFileName
    Dim Stream As New System.IO.FileStream(streamSourcePath, FileMode.OpenOrCreate, FileAccess.Write)

    encoder.Save(Stream)
    Stream.Close()
    Stream = Nothing

    bmpImage.BeginInit()
    bmpImage.CreateOptions = BitmapCreateOptions.IgnoreImageCache
    bmpImage.CacheOption = BitmapCacheOption.OnLoad
    bmpImage.UriSource = New Uri(streamSourcePath)
    bmpImage.EndInit()

    Return bmpImage
End Function

And here is the RotateImage() implementation which runs behind a button click event:

Public Sub RotateImage()

    ' Store our source rect for use in applied transforms
    Dim ImageRect As New System.Drawing.RectangleF(System.Drawing.PointF.Empty, New System.Drawing.SizeF(Me._bmpSource.Width, Me._bmpSource.Height))

    ' Store our target rect for use in applied transforms
    ' NOTE: For this demo we're always rotating 90 degrees so just flip height/width
    Dim CanvasRect As System.Drawing.RectangleF = New System.Drawing.RectangleF(System.Drawing.Point.Empty, New System.Drawing.SizeF(Me._bmpSource.Height, Me._bmpSource.Width))

    ' Figure out the point in which we will need to draw the image
    ' to fit it within the bounds of our canvas rectangle
    Dim WidthOffset As Decimal = (CanvasRect.Width - ImageRect.Width) / 2
    Dim HeightOffset As Decimal = (CanvasRect.Height - ImageRect.Height) / 2

    ' Build transform needed for rotate
    Dim transformActions As New System.Windows.Media.TransformGroup
    transformActions.Children.Add(New System.Windows.Media.RotateTransform(90, Me._bmpSource.Width / 2, Me._bmpSource.Height / 2))
    transformActions.Children.Add(New System.Windows.Media.TranslateTransform(WidthOffset, HeightOffset))

    ' Apply the transformation to the image
    Dim transformedBitmap As New System.Windows.Media.Imaging.TransformedBitmap(Me._bmpSource, transformActions)

    ' Trying to break references here so things will get disposed but it isn't working
    If TypeOf Me._bmpSource Is BitmapImage Then
        CType(Me._bmpSource, BitmapImage).StreamSource = Nothing
    ElseIf TypeOf Me._bmpSource Is TransformedBitmap Then
        CType(Me._bmpSource, TransformedBitmap).Source = Nothing
    End If

    ' Breaking main ref also doesn't help
    Me._bmpSource = Nothing

    ' Garbage collection does nother either
    GC.WaitForPendingFinalizers()
    GC.Collect()

    Me._bmpSource = transformedBitmap
    Me._Canvas.Background = New System.Windows.Media.ImageBrush(Me._bmpSource)
    Me._Canvas.UpdateLayout()
End Sub

回答1:


GC somehow didn't have enough time to release memory. Add this code at the end of your method or in each loop.

System.GC.Collect()
System.GC.WaitForPendingFinalizers()


来源:https://stackoverflow.com/questions/18453464/memory-leak-when-updating-wpf-canvas-background

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!