views:

391

answers:

2

This is interesting. We've spent the last day attempting to patch a problem with the following (legacy) code that continues to grow its process size. This is done in Visual Studio 2003.

We have a form on which we display an image (from MemoryStream) and some text and a button. Nothing fancy. Looks something like this:

  Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
    MyBase.OnLoad(e)
    Try
      m_lblWarning.Visible = False

      m_grpTitle.Text = m_StationInterface.ProcessToolTitle
      m_lblMessage.Text = m_StationInterface.ProcessToolMessage

      Dim objImage As MemoryStream
      Dim objwebClient As WebClient
      Dim sURL As String = Trim(m_StationInterface.ProcessToolPicLocation)

      objwebClient = New WebClient

      objImage = New MemoryStream(objwebClient.DownloadData(sURL))
      m_imgLiftingEye.Image = Image.FromStream(objImage)

      m_txtAcknowledge.Focus()
    Catch ex As Exception
      '*** This handles a picture that cannot be found without erroring'
      m_lblWarning.Visible = True
    End Try
  End Sub

This form is frequently closed and opened. Each time it is re-opened, the process memory usage grows by roughly 5mb. When the form is closed, it doesn't drop back down to its previous usage. The resources remain allocated for the unreferenced form. The form is rendered like this:

      m_CJ5Form_PTOperatorAcknowlegement = New CJ5Form_PTOperatorAcknowlegement
      m_CJ5Form_PTOperatorAcknowlegement.stationInterface = m_StationInterface
      m_CJ5Form_PTOperatorAcknowlegement.Dock = DockStyle.Fill
      Me.Text = " Acknowledge Quality Alert"

      '*** Set the size of the form'
      Me.Location = New Point(30, 30)
      Me.Size = New Size(800, 700)

      Me.Controls.Add(m_CJ5Form_PTOperatorAcknowlegement)

The control is later removed from the form after closing:

Me.Controls.Clear()

Now. We've tried very numerous things. We've discovered that Disposing does nothing, and, indeed, the IDisposable interface doesn't actually touch memory. If we don't create a new CJ5Form_PTOperatorAcknowledgement form each time, the process size does NOT grow. But loading a new image into that form still causes the process size to continually grow.

Any suggestions would be appreciated.

A: 

I don't know why specifically that is leaking, but I can recommend that you look at using the .NET Memory Profiler. If you run your application using that, it will give you a very good idea of what objects are not being disposed and help you troubleshoot why. It's got a free trial, but it's well worth the purchase.

JP Alioto
+2  A: 

You have to dispose of your WebClient object and any other managed unmanaged resources that may no longer be needed.


objImage = New MemoryStream(objwebClient.DownloadData(sURL))
objwebClient.Dispose() ' add call to dispose

An even better way to code that would be to use a 'using' statement:


using objwebClient as WebClient = New WebClient      
objImage = New MemoryStream(objwebClient.DownloadData(sURL))      
end using

For further information please search for 'Dispose' and 'IDisposable' pattern implementation on google and stackoverflow.

One last hint: Don't use a memory stream if possible. Load the image directly from file unless you need to keep it in RAM.

Edit

If I understand your code correctly perhaps something like this would work:


Dim objImage As MemoryStream      
Dim objwebClient As WebClient      
Dim sURL As String = Trim(m_StationInterface.ProcessToolPicLocation)      
using objwebClient as WebClient = New WebClient      
  using objImage as MemoryStream = New MemoryStream(objwebClient.DownloadData(sURL))      
    m_imgLiftingEye.Image = Image.FromStream(objImage)
  end using
end using
The "using" statement wasn't added until Visual Studio 2005. Disposing is not the problem here. We've tried absolutely every variation of object disposal without success. We've also tried storing a local copy of the file rather than using a memory stream.
hypoxide
Are you using COM objects? You may need to call System.Runtime.InteropServices.Marshal.ReleaseComObject on your COM objects?
No COM objects involved. The ONLY thing that has worked so far is reusing the same form object instead of instantiating a new one each time. That would be a solution if we didn't have to load a new image onto the form each time (which still grows process size). It's as if the garbage collector doesn't recognize that the old object is no longer referenced. We can even leave out any functional stuff on the form and the process size still grows.
hypoxide
Well there's way to many variables and not enough context for me to keep guessing here, but I would say did you try Form.Dispose, Image.Dispose etc, etc, ... Any resource you 'new' you should 'dispose.' Also the garbage collector runs in it's own time and you will not see an immediate release of memory. If you have overridden 'finalize' it may prolong the retention of that memory.
In addition to disposing of the form etc... ensure you manually remove any event handlers that you added using 'addhandler.'
Thanks. I appreciate having some new eyes on the problem.
hypoxide