views:

422

answers:

5

Is there something that needs to be done with the following code to release the memory it uses?

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

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

The code is on a popup form that shouldn't ever get disposed. A new image is loaded onto the form every time it pops up. However, the process size for the application continues to grow each time it makes it through that code block.

I've tried objImage.Close() and .Flush(), objWebClient.Dispose(). The process size still grows by a good 4mb after every call. It's like the old image is kept in memory.

A: 

You could try

set objImage = nothing
set objwebClient = nothing

Often, like with ADO, if you don't explicitly set it to nothing it doesn't get released properly.

David McEwing
Just tried it. Memory continues to grow.
hypoxide
+2  A: 

MemoryStream implements the IDisposable interface, so you should call Dispose on that object when you are done using it:

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

I would guess your conclusion was right; the image (in the memory stream) does remain in the memory.

Update: as Marc pointed out Image.FromStream requires the stream to remain open for the lifetime of the image. To resolve this the MemoryStream variable should be declared in same scope as the image (as a field in the form). When loading the image, there should first be a check whether the MemoryStream already is open and if so, close and dispose it before using the variable for a new stream (let's assume that we call it m_imageStream). Since the image also implements IDisposable, the same is true for that one:

If Not m_imageStream Is Nothing Then
    m_imageStream.Dispose()
End If

If m_imgLiftingEye.Image Is Not Nothing Then
    m_imgLiftingEye.Image.Dispose()
End If

m_imageStream = New MemoryStream(objwebClient.DownloadData(sURL))
m_imgLiftingEye.Image = Image.FromStream(m_imageStream)
Fredrik Mörk
I don't believe MemoryStream implements IDisposable.
hypoxide
That was my first thought too, but looking at the Image class' FromStream() documentation: "You must keep the stream open for the lifetime of the Image." Which is no good; I'd suggest caching the image and loading the Image class from that, rather than holding around multiple objects that need disposal at some point in the request lifecycle.
Marc Bollinger
http://msdn.microsoft.com/en-us/library/system.io.memorystream.aspx
hypoxide
hypoxide: System.IO.Stream implements IDisposable, which MemoryStream inherits.
Marc Bollinger
I have updated my post with a code sample that I tried out, and found to work. R. Bemrose and I was sort of attacking the same target, but on different objects: both the Image and the Stream need to disposed. (haven't written VB code in years, that was an interesting experience)
Fredrik Mörk
I see what you're doing there, but what is m_imageStream? Also, the MemoryStream gets declared right there. It should start as nothing, I would think?
hypoxide
I renamed objImage to m_imageStream, since I suggested to move its declaration out from the method up to the class level, so that it lives between calls to the method that loads the image. That way the stream is kept alive, but we also keep a reference to it so that it can be properly disposed. I can update the post with a full class sample, if you wish.
Fredrik Mörk
+2  A: 

Try this:

Function GetImage() As Image
    Using wc As New WebClient(), _
          ms As New MemoryStream(wc.DownloadData(m_StationInterface.PicLocation.Trim())

        GetImage = Image.FromStream(ms).Clone()
    End Using
End Function
Joel Coehoorn
I gave that a shot. Doesn't work.
hypoxide
Cloning was a clever idea, by the way. So was making a function.
hypoxide
+3  A: 

Image implements IDisposable, so you should Dispose the old image before replacing it with a new one.

Something like (bear with me, I haven't used VB in a while):

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

objwebClient = New WebClient
objImage = New MemoryStream(objwebClient.DownloadData(sURL))

If m_imgLiftingEye.Image Is Not Nothing Then
    m_imgLiftingEye.Image.Dispose()
End If
m_imgLiftingEye.Image = Image.FromStream(objImage)
R. Bemrose
I've tried that already and it didn't work.
hypoxide
My code looks exactly like that and the process size still grows.
hypoxide
I think he needs to do this _and_ my answer.
Joel Coehoorn
+1  A: 

I know I already gave one answer, but I've been thinking since then...

You said that this form should never be disposed. In that case, when exactly is this image load happening? My previous answer assumed it was during the form Shown event. However, if it's during the form Load event, it should only happen once total.

That is, unless more than one instance of the form is being created. If that's the case, and the previous form isn't being reused, you're ending up with multiple copies of the same form loaded in memory, each with its own copy of the image.

R. Bemrose
I thought about that as well, but the only place the dialog form is being created is during load.
hypoxide
Could also be the Activated event.
Joel Coehoorn