HttpRequest/HttpResponse 内存泄漏? CF.NET 3.5 WIN CE 6.0
我已经尝试了一切可能的方法来消除我认为的内存泄漏在 Win CE 6.0 设备上运行的 CF.NET 3.5 中的 HttpRequest 或 HttpResponse 类。我正在使用它们与 IP 摄像机进行通信。
以下是我当前使用的代码。该代码在线程上的自定义控件中运行,其优先级设置为低于正常值,backgroundworker 设置为 true。我的一个窗体上有两个这样的控制对象。
我说当前是因为我尝试了异步请求和以下代码的其他排列,但没有减少内存消耗:
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);
}
}
我已经使用 RPM 对其进行了分析,并且随着内存的增长,对 System.Net 命名空间进行了一些根对象的分析,并且System.Threading 命名空间,其中包括一堆我没有创建的线程和同步对象。
我附上了第一个和最后一个堆快照的堆比较图像。
我已确保使用“using”并对所有允许它的对象调用 dispose。另外,我确保完成后中止请求。我在其他示例中看到过这一点,它应该释放连接资源等。
这是奇怪的部分,只有当我没有连接相机时抛出超时 WebException 时才会发生泄漏。连接相机后,设备可以运行数天而无需增加内存。此外,托管字节数和总字节数都以 RPM 的形式增长,因此我认为这不是非托管泄漏。最后,我试图尽快从相机获取图像。我开始怀疑我是否只是没有给 GC 时间来收集。但是,当发生收集时(我看到收集计数在 RPM 中增加),托管字节计数不会减少,它只是不断增长。希望我做了一些非常愚蠢的事情,这是一个很容易解决的问题。一如既往,我们感谢任何帮助或建议。
附加信息:
从相机线程调用的两个委托如下,如果可能有助于了解:
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();
}
附加信息2:
只是为了完成信息,下面我已经包含了 OnPaint() 等。我用来将位图绘制到相机屏幕:
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)
{
}
更新:
这是我没有得到的。由于 HttpRequest 只是一个保存信息的对象,无法关闭/处置,而且当抛出超时 WebException 时,HttpResponse 仍然为 null(无法关闭),那么什么是引用用于尝试请求的资源呢?唯一的解释是 HttpRequest 对象持有一些引用,当调用 Abort 时,应该释放用于发出请求的资源,我在 RPM 中看到的资源没有恢复。由于我调用了 Abort() 并且 HttpRequest 对象仅在请求期间位于范围内,因此我看不出如何无法收集所引用的任何资源。
Update2:
嗯,我让它在启用摄像头的情况下运行,并允许超时继续,然后我禁用摄像头,消除 HttpRequest 尝试和超时,并让它在当天剩余的时间内运行。最终,GC 停留在相同的值(根据过去的测试,它应该增长了大约 6MB),证明这与给 GC 时间收集无关,至少我认为是这样。所以资源仍然悬而未决,我需要弄清楚到底是什么让它们扎根。希望我能想到这一点 出来并提供另一个更新。在那之前...
旁注:
是否有人曾经在使用 CF.NET 3.5 的 WIN CE 设备上使用 HttpRequest/HttpResponse 从 IP 摄像机获取图像?如果是这样,是否存在相机无限期失去通信的测试用例?这应该是我问的第一件事,因为我没有找到很多示例来展示如何从嵌入式设备与 IP 摄像机进行通信。
Update3:
嗯,我想我已经偶然发现了针对我的特定问题的解决方案。我对 ServicePointManager 静态类成员的默认连接数和最大空闲时间进行了一些更改:
ServicePointManager.DefaultConnectionLimit = 4;
ServicePointManager.MaxServicePointIdleTime = 1000;
因为我随时最多连接 4 个摄像头,并且由于我的 HttpRequest 超时设置为 5000 毫秒,我想我会尝试 1000 毫秒的最大空闲时间,看看会发生什么。我让两个设备在没有连接摄像头的情况下运行过夜(每 5000 毫秒超时)。通常会发生的情况是,我早上来时,设备会坐在那里,并显示 OOM 消息,并且我的系统的 GC 内存和物理内存将达到最大。嗯,两台设备的内存水平与我昨晚离开时的内存水平相同。所以,我希望这能解决我的问题。基于MSDN文档:
ConnectionLimit 属性设置 ServicePoint 可以与 Internet 资源建立的最大连接数。创建 ServicePoint 时,ConnectionLimit 属性的值设置为 ServicePointManager.DefaultConnectionLimit 属性的值;对 DefaultConnectionLimit 的后续更改对现有 ServicePoint 实例没有影响。
MaxIdleTime 属性包含在将 Internet 资源回收用于另一个连接之前允许 ServicePoint 保持与 Internet 资源的空闲连接的时间长度(以毫秒为单位)。您可以将 MaxIdleTime 设置为 Timeout.Infinite 以指示 ServicePoint 永远不应超时。 MaxIdleTime 属性的默认值是创建 ServicePoint 时 ServicePointManager.MaxServicePointIdleTime 属性的值。对 MaxServicePointIdleTime 属性的后续更改对现有 ServicePoint 实例没有影响。
MaxServicePointIdleTime 属性设置 ServicePointManager 在创建 ServicePoint 实例时分配给 MaxIdleTime 属性的最大空闲时间。对此值的更改只会影响该值更改后初始化的 ServicePoint 实例。当 ServicePoint 空闲达到 MaxIdleTime 中指定的时间后,它就可以进行垃圾回收。当与 ServicePoint 关联的连接列表为空时,ServicePoint 处于空闲状态。
对我来说,所有这一切的关键是,特别指出,在服务点空闲达到最大空闲时间后,它就有资格进行垃圾收集。我见过 100 秒到 900 秒作为该值的默认值,具体取决于描述所涉及的框架版本。在我认为这是修复之前,我将进行更多测试。我很乐意从任何使用过这些属性的人那里解决他们的具体问题,以及这是否有意义作为我所看到的问题的根本原因。
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
只需将
HttpWebRequest
对象的AllowWriteStreamBuffering
属性设置为 false:simply set the
AllowWriteStreamBuffering
property of yourHttpWebRequest
object to false:请参阅帖子中的Update3。由于列出的原因,这似乎解决了我的问题。感谢您的回复。
Please refer to Update3 in the post. This seems to have fixed my issue because of the reasons listed. Thanks for the responses.