Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HttpRequest/HttpResponse Memory Leak? CF.NET 3.5 WIN CE 6.0

RPM Heap Compare

I have tried everything possito get rid of what I think is a memory leak with the HttpRequest or HttpResponse Classes in the CF.NET 3.5 running on a Win CE 6.0 device. I am using them to communucate with an IP camera.

Below is the current code I am using. The code is running in a custom control on a thread with its priority set to below normal and backgroundworker set to true. There are two of these control objects on one of my forms.

I say current because I have tried async requests and other permutations of the below code with no reduction in memory consumption:

    protected void CamRefreshThread()
    {
        while (true)
        {
            if (false != CamEnabled)
            {
                HttpWebRequest  HttpReq = null;

                try
                {
                    lock (LockObject)
                    {
                        // create request
                        HttpReq = (HttpWebRequest)WebRequest.Create("http://" + this.Ipv4Address + "/axis-cgi/jpg/image.cgi");
                        HttpReq.Timeout = 5000;
                        HttpReq.ReadWriteTimeout = 5000;
                        HttpReq.Credentials = new NetworkCredential(this.CamUserName, this.CamPassword);
                    }

                    /* indicate waiting for reponse */
                    ResponseRxed = false;
                    // get response
                    using (HttpWebResponse HttpResp = (HttpWebResponse)HttpReq.GetResponse())
                    {
                        // get response streamImageFromStream
                        using (Stream ImgStream = HttpResp.GetResponseStream())
                        {
                            // get bitmap
                            using (Bitmap ImgFrmStream = new Bitmap(ImgStream))
                            {
                                if (false != CamEnabled)
                                {
                                    /* indicate response has not timed out */
                                    ResponseTimedOut = false;
                                    ResponseFirst = true;
                                    // marshall bitmap
                                    this.Invoke(GetBitmapDelegate, ImgFrmStream);
                                    /* indicate response rxed */
                                    ResponseRxed = true;
                                }
                            }
                        }
                    }
                }
                catch (WebException e)
                {
                    if (false == ResponseTimedOut)
                    {
                        ResponseTimedOut = true;
                        ResponseFirst = false;
                        this.Invoke(RefreshDisplayDelegate);
                    }
                }
                catch (Exception)
                {

                }
                finally
                {
                    if (null != HttpReq)
                    {
                        HttpReq.Abort();
                    }
                }
            }

            Thread.Sleep(1);
        }
    }

I have profiled it with the RPM and as the memory grows, so do a butch of rooted objects for the System.Net namespace and System.Threading namespace, which includes a bunch of thread and sync objects that I am not creating.

I have attached an image of the heap comparison of the first and last heap snapshots.

I have made sure to use "using" and call dispose on all the ojects that allow it. Also, I make sure to Abort the request when done. I have seen this in other examples and it is supposed to release connection resources, etc.

Here is the strange part, the leak only occurs when a timeout WebException is thrown if I do not have cameras connected. With cameras connected, the devices runs for days without increase in memory. Also, both the managed number of bytes and the total number of bytes grows in RPM so I do not think it is an unmanged leak. Lastly, I am attempting to get images from the camera as fast as I can. I am starting to wonder if I am just not giving the GC time to collect. But when a collection occurs (I see the collection count go up in RPM) the managed byte count does not go down, it just keeps growing. Hopefully I am doing something very stupid and this is an easy fix. As always, any help or suggestions are appreciated.

Additional Info:

The two delegates invoked from the camera thread are as follows if it might help to know:

GetBitmapDelegate = new VoidDelegateBitmap(UpdateCamImage);
RefreshDisplayDelegate = new VoidDelegateVoid(RefreshCamImage);

protected void UpdateCamImage(Bitmap Frame)
{
    if (null != BmpOffscreen)
    {
        BmpOffscreen.Dispose();
    }

    BmpOffscreen = (Bitmap)Frame.Clone();
    Refresh();
}

protected void RefreshCamImage()
{
    Refresh();
}

Additional Info2:

Just to complete the information, below I have included the OnPaint(), etc. That I used to paint the Bitmap to the screen for the camera:

protected override void OnPaint(PaintEventArgs e)
{
    string DisplayString = null;

    if (false == CamEnabled)
    {
        DisplayString = string.Empty;
    }
    else if (false != ResponseTimedOut)
    {
        DisplayString = "Communication Timeout!";
    }
    else if ((null != BmpOffscreen) && (false != ResponseFirst))
    {
        e.Graphics.DrawImage(BmpOffscreen, 0, 0);
    }
    else
    {
        DisplayString = "Loading...";
    }

    if (null != DisplayString)
    {
        e.Graphics.Clear(this.BackColor);

        using (SolidBrush StringBrush = new SolidBrush(this.ForeColor))
        {
            using (StringFormat Format = new StringFormat())
            {
                Format.LineAlignment = StringAlignment.Center;
                Format.Alignment = StringAlignment.Center;
                e.Graphics.DrawString(DisplayString, this.Font, StringBrush, this.ClientRectangle, Format);
            }
        }
    }
}

protected override void OnPaintBackground(PaintEventArgs e)
{

}

Update:

Here is what I do not get. Since HttpRequest is just an object that hold information and cannot be closed/disposed, and since when a timeout WebException is thrown the HttpResponse is still null (cannot be closed), what is referencing the resources that were used to attempt the request? The only explaination is that there is some reference held by the HttpRequest object that when Abort is called, should release the resources used to make the request, the ones that I am seeing not recovered in RPM. Since I call Abort() and since the HttpRequest object is only in the scope during the request, I do not see how any resources referenced cannot be collected.

Update2:

Well, I let it run with the cameras enabled and allowed the timeouts to continue, then I disabled the cameras, eliminating the HttpRequest attempts and the timeouts, and let it run the rest of the day. At the end of the day, the GC was stuck at the same value (based on past test it should have grown by about 6MB), proving that is has nothing to do with giving the GC time to collect, at least I think. So the resources are still off in limbo and I need to figure out exactly what is keeping them rooted. Hopefully I can figure that out and give another update. Until then...

Side Note:

Has anyone ever used HttpRequest/HttpResponse to get images from an IP camera, on a WIN CE device using CF.NET 3.5? If so, was there a test case for loss of comm from the camera for an indefinite amount of time? That should have been the first thing I asked since I have not found a lot of examples out there showing how to communicate to IP cameras from embedded devices.

Update3:

Well I think I have stumbled onto a fix for my specific issue. I made a few changes to the ServicePointManager static class members with respect to the number of default connections and the max idle time:

ServicePointManager.DefaultConnectionLimit = 4;
ServicePointManager.MaxServicePointIdleTime = 1000;

Since I will have a maximum of 4 cameras connected at any time, and since my timeout for the HttpRequest is set to 5000ms, I figured I would try a 1000ms max idle time to see what would happend. I let two units run overnight with no cameras connected (timing out every 5000ms). What normally would happen is I would come in the morning and the devices would be sitting there with an OOM message and the GC memory and physical memory would be maxed out for my system. Well, both devices were at the same memory levels that they were at when I left last night. So, I am hoping this is the fix for my issue. Based on MSDN documentation:

The ConnectionLimit property sets the maximum number of connections that the ServicePoint can make to an Internet resource. The value of the ConnectionLimit property is set to the value of the ServicePointManager.DefaultConnectionLimit property when the ServicePoint is created; subsequent changes to DefaultConnectionLimit have no effect on existing ServicePoint instances.

The MaxIdleTime property contains the length of time, in milliseconds, that the ServicePoint is allowed to maintain an idle connection to an Internet resource before it is recycled for use in another connection. You can set MaxIdleTime to Timeout.Infinite to indicate that the ServicePoint should never timeout. The default value of the MaxIdleTime property is the value of the ServicePointManager.MaxServicePointIdleTime property when the ServicePoint is created. Subsequent changes to the MaxServicePointIdleTime property have no effect on existing ServicePoint instances.

The MaxServicePointIdleTime property sets the maximum idle time that the ServicePointManager assigns to the MaxIdleTime property when creating ServicePoint instances. Changes to this value will affect only ServicePoint instances that are initialized after the value is changed. After a ServicePoint has been idle for the time specified in MaxIdleTime, it is eligible for garbage collection. A ServicePoint is idle when the list of connections associated with the ServicePoint is empty.

The key to all of this for me is that is specifically states that after the service point is idle for the max idle time it is eligible for garbage collection. I have seen anywhere from 100 secs to 900 secs as the default for this value, depending on the framework version the description was pertaining to. I am going to do some more testing before I consider this a fix. I would love to here from anyone who has played with these properties to fix their specific issues and whether or not this makes sense as the root cause of the problem I have been seeing.

like image 942
CCS Avatar asked Aug 18 '11 22:08

CCS


2 Answers

simply set the AllowWriteStreamBuffering property of your HttpWebRequest object to false:

HttpReq.AllowWriteStreamBuffering = false;
HttpReq.AllowAutoRedirect = false;
like image 67
Yahya Younes Avatar answered Sep 23 '22 21:09

Yahya Younes


Please refer to Update3 in the post. This seems to have fixed my issue because of the reasons listed. Thanks for the responses.

like image 35
CCS Avatar answered Sep 25 '22 21:09

CCS