Memory leak

  • 13 years ago
    Hi all,

    I am trying to rotate an image in a picturebox with images from resources.
    I have 20 images which I swap every 50 ms.
    I do that like this:

    Private Sub ChangeLogo()
            Dim logo As Image
            Dim logonumber1 As New System.Text.StringBuilder
            Do
                logonumber1.Remove(0, logonumber1.Length)
                logonumber1.Append("Logo")
                logonumber1.Append(k.ToString.PadLeft(2, "0"))
                 logo = CType(My.Resources.Resources.ResourceManager.GetObject(logonumber1.ToString), System.Drawing.Bitmap)
                PictureBoxVisible(Me.PictureBox2, logo)
                Thread.Sleep(50)
                k += 1
                k = k Mod 20
            Loop
    End Sub

    the problem is in the red colored line of code, that part is making the system consume memory until it runs out and exits with memory exception unhandled.

    Does anyone have any idea how to do that so it doesn't consume that much memory???
    Thanks,

    Grega





























  • 13 years ago

    Assuming you do not run out of memory the first time the line in red is run, try adding this line before it:

    logo.dispose

     

    You may need to set the picturebox to be empty first.

     

  • 13 years ago
    Hi,
    thanks for reply.

    No I don't run out of memory for a while but after a while, when  images in a picturebox already come around for a few times.

    I put the logo.dispose before and emptied the picturebox before like this...
    '*****************************
    PictureBoxEmpty(Me.PictureBox2)
    If Not (logo Is Nothing) Then
        logo.Dispose()
    End If
    logo = CType(My.Resources.Resources.ResourceManager.GetObject(logonumber1.ToString),                 System.Drawing.Bitmap)
    PictureBoxVisible(Me.PictureBox2, logo)
    '*****************************

     Private Delegate Sub PictureBoxEmptyDelegate(ByVal Pbx As PictureBox)

        Dim PictureBoxEmpty1 As PictureBoxEmptyDelegate

        Private Sub PictureBoxEmpty(ByVal Pbx As PictureBox)
            If Me.InvokeRequired Then
                PictureBoxEmpty1 = New PictureBoxEmptyDelegate(AddressOf PictureBoxEmpty)
                Invoke(PictureBoxEmpty1, Pbx)
            Else
                Pbx.Image = Nothing
            End If
        End Sub
    '********************************


    but it doesn't help...I guess after a while garbage colector frees all the old logo objects, but that doesn't happen fast enough.

    Anys other ideas?
    Thanks
    Grega



































  • 13 years ago
    You added this code
    If Not (logo Is Nothing) Then
        logo.Dispose()
    End If
    But is 'logo' still a local variable or did you change it to a global one? This code won't do anything if 'logo' is still local because 'logo' wouldn't maintain a reference to the last image it pointed to. Try making it global if it isn't and see what happens. You could also try loading the imgages into an array ahead of time. Instead of createing a new image each time from the resources and presumably requiring more and more memory, you would store all the images you'll need in an array. Now you don't have to create a new instance of a image every 50 ms, you just need to grab the appropriate reference when they are needed.
  • 13 years ago

    I ran a little test.  Basically what I did was try to simulate your scenerio.  I had a timer fire off every 50 ms.  When it fired it created a new instance of an image (it picked a file on my HD at random) and assigned it to a picturebox.  I watched my availalbe memory by bringing up the task manager.  Available memory would continue to drop upto about 300-400 mB at which point it appeared the garbage collector ran and it would jump back up.  It cycled like this for as long as my test ran.

    I had about a 1 gig of free memory so my system could handle it but obviosly this isn't a good thing.  I then added one line of code.  Before I assign the image to the picturebox I call the dispose method of the image in the picturebox.  It looked like this.

     

            If PictureBox1.Image IsNot Nothing Then PictureBox1.Image.Dispose()
            PictureBox1.Image = Image.FromFile(file)

    After doing this memory usage stopped fluctuating. So my test shows calling the dispose method for the image should solve the problem. So either make sure 'logo' is declared globally and thus remembers it's reference to the last image you created or get the image reference by using Picturebox1.image and call the dispose method when your done with it.

    **EDIT**

    I noticed in your procedure 'PictureBoxEmpty' you set the image = nothing.  This puts you at the mercy of the garbage collector.  Whenever you want to free a resource immediatly you should call it's dispose method.  Basically if your done with an object and it has a dispose method you should always call it.  Assuming you are calling PictureBoxEmpty before assigning the new image you should be able to fix the memory leak just by changing pbx.image = nothing to pbx.image.dispose

  • 13 years ago
    Hi,
    Thanks a lot for your reply TwoFaced. That made few things more clear to me! :)
    the thing is that I think that unstopable consumtion of has now stoped. (the whole think is in my case even more problematic becouse this is application for embedded system, so I am very limited with memory I have something like 14mb free)
    I used the first solution, declared logo as global and disposed it before setting a new image to it, becouse the second I cannot make it work I tried like this:
       Dim PictureBoxVisible1 As New PictureBoxVisibleDelegate(AddressOf PictureBoxVisible)

        Private Sub PictureBoxVisible(ByVal ctr As Control, ByVal img As Image)
            If ctr.InvokeRequired Then
                BeginInvoke(PictureBoxVisible1, ctr, img)
            Else
                Dim pbx As PictureBox
                If TypeOf (ctr) Is PictureBox Then
                    pbx = ctr
                    If pbx.Image IsNot Nothing Then pbx.Image.Dispose()
                    pbx.Image = img
                End If
                ctr.Visible = True
            End If
        End Sub
    but in a few repeats I get ObjectDisposedException was unhandled! message.
    I think that memory still rises till it loops trough all the images and then stops?!(EDIT:memory goes up by 64KB for a while) but I'm not sure. Since I am doing all this in non GUI thread and I am accessing the control on the form trough the delegate, do you think that the way I do that could be a problem to?

    Thanks for all your help!
    Grega
























  • 13 years ago

    I don't think threading should be a problem.  I set up the scenerio again only this time I used a thread like you.  I wasn't able to re-create your error and memory usage seemed reasonable.  My system was fluctuating by about 5-10 MB while the application was running but it would also do this at times when the application wasn't running.  So it's hard to say what additional memory usage my demo actually used.

    If memory is only going up 64 KB then that's hardly worth a concern.  Even with a limited environment of 14 MB that's not very much.  Plus some memory has to be used becaues you have an image in memory all the time. 

    As for the error I had a few guesses as to why that might occur and tried to recreate it but I just couldn't.  Try changing BeginInvoke to Invoke.  It's worth a shot but in my test either worked fine.  Also take a look at the code I used and see if you can figure out any possible differences. 

    Public Class Form1
        Private th As System.Threading.Thread
    
        ' Start the logo animation
        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            th = New System.Threading.Thread(AddressOf ChangeLogo)
            th.Start()
        End Sub
    
        ' Stop the logo animation
        Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
            th.Abort()
        End Sub
    
        Private Sub ChangeLogo()
            Do
                Dim logo As Image = GetNewImage()
                SetPBImage(PictureBox1, logo)
                Threading.Thread.Sleep(50)
            Loop
        End Sub
    
        Private Delegate Sub SetPBImageDelegate(ByVal pb As PictureBox, ByVal logo As Image)
        ' Sets the image for a given picturebox in a thread safe manor
        Private Sub SetPBImage(ByVal pb As PictureBox, ByVal logo As Image)
            If pb.InvokeRequired Then
                pb.BeginInvoke(New SetPBImageDelegate(AddressOf SetPBImage), pb, logo)
            Else
                ' Dispose of the previous image so resources are freed immediatly
                If pb.Image IsNot Nothing Then pb.Image.Dispose()
                pb.Image = logo
            End If
        End Sub
    
        Private rnd As New Random
        ' Returns a random image from a directory
        Private Function GetNewImage() As Image
            Dim files As String() = IO.Directory.GetFiles("c:\logos", "*.jpg")
            Dim file As String = files(rnd.Next(0, files.Length))
    
            Return Image.FromFile(file)
        End Function
    End Class

    With my code I get a random image from the hard drive. I don't think this should make a difference.

    **EDIT**
    If you are still having troubles post all the code that handles changing the logo and I'll see if I can spot a problem.

  • 13 years ago

     Hi,

    Thanks for all this Two faced... helped me a lot.

     I managed to clear most of the memory leak now.   those 64K changed into around 16K per day...so it is much better but still since this will be application that will need to run not for few days but few years this is still quite an issue.

    I am doing a lot of testing by letting application run for long time few days, and everytime I exclude something
    In code I pasted below is where I write some data into some text file... this is done every 2,5 minute by calling all three in a sequent order:

    f.OpenFile()
    f.WriteDataToFile(data.ToString)
    f.CloseFile()

     Does anyone think this could be the problem for the small leak I am still having...(leak goes up by 4K)

     Thanks a lot for your help,

    Grega
     

        Public Sub OpenFile() 'ByVal path As String
            Dim path As String = DiskPath & "\" & "File_" & Format(Now, "yyyyMMdd") & ".txt"
            'Dim path As String = "C" & "\" & "File_" & Format(Now, "yyyyMMdd") & ".txt"
            fs = New FileStream(path, FileMode.OpenOrCreate, FileAccess.Write)
            s = New StreamWriter(fs)
        End Sub

        Public Sub CloseFile()
            s.Close()
            fs.Close()
            s = Nothing
            fs = Nothing
        End Sub

        Public Sub WriteDataToFile(ByVal data As String)
            s.BaseStream.Seek(0, SeekOrigin.End)
            s.WriteLine(data)
        End Sub

     

Post a reply

Enter your message below

Sign in or Join us (it's free).

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law.”