【发布时间】:2011-08-18 22:35:19
【问题描述】:
我已经尝试了一切,以消除我认为在 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 命名空间的一些根对象也是如此,其中包括一堆我没有创建的线程和同步对象。
我附上了第一个和最后一个堆快照的堆比较图像。
我确保在所有允许它的对象上使用“使用”并调用 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 仍然为空(无法关闭),所以引用用于尝试请求的资源是什么?唯一的解释是 HttpRequest 对象持有一些引用,当调用 Abort 时,应该释放用于发出请求的资源,我看到的资源在 RPM 中没有恢复。由于我调用了 Abort() 并且由于 HttpRequest 对象仅在请求期间的范围内,因此我看不到如何无法收集引用的任何资源。
更新2:
好吧,我让它在启用摄像头的情况下运行并允许超时继续,然后我禁用摄像头,消除 HttpRequest 尝试和超时,让它在一天的剩余时间里运行。最终,GC 停留在相同的值(根据过去的测试,它应该增长了大约 6MB),证明这与给 GC 收集时间无关,至少我认为。所以资源仍然处于困境中,我需要弄清楚是什么让它们根深蒂固。希望我能想到 出来并给出另一个更新。到那时……
旁注:
有没有人在使用 CF.NET 3.5 的 WIN CE 设备上使用 HttpRequest/HttpResponse 从 IP 摄像机获取图像?如果是这样,是否有一个测试案例可以无限期地从相机中丢失通信?这应该是我问的第一件事,因为我没有找到很多展示如何从嵌入式设备与 IP 摄像头通信的示例。
更新3:
好吧,我想我偶然发现了针对我的特定问题的修复方法。关于默认连接数和最大空闲时间,我对 ServicePointManager 静态类成员进行了一些更改:
ServicePointManager.DefaultConnectionLimit = 4;
ServicePointManager.MaxServicePointIdleTime = 1000;
由于我在任何时候最多连接 4 个摄像头,并且由于我的 HttpRequest 超时设置为 5000 毫秒,我想我会尝试设置 1000 毫秒的最大空闲时间来看看会发生什么。我让两个单元在没有连接摄像头的情况下整夜运行(每 5000 毫秒超时)。通常会发生的情况是我早上来,设备会坐在那里并显示 OOM 消息,并且我的系统的 GC 内存和物理内存将被最大化。好吧,这两款设备的内存水平与我昨晚离开时的内存水平相同。所以,我希望这是解决我的问题的方法。基于 MSDN 文档:
ConnectionLimit 属性设置 ServicePoint 可以与 Internet 资源建立的最大连接数。 ConnectionLimit 属性的值在创建 ServicePoint 时设置为 ServicePointManager.DefaultConnectionLimit 属性的值;对 DefaultConnectionLimit 的后续更改对现有的 ServicePoint 实例没有影响。
MaxIdleTime 属性包含时间长度,以毫秒为单位,在服务点被回收以用于另一个连接之前,它被允许保持与 Internet 资源的空闲连接。您可以将 MaxIdleTime 设置为 Timeout.Infinite 以指示 ServicePoint 永远不会超时。 MaxIdleTime 属性的默认值是创建 ServicePoint 时 ServicePointManager.MaxServicePointIdleTime 属性的值。对 MaxServicePointIdleTime 属性的后续更改对现有 ServicePoint 实例没有影响。
MaxServicePointIdleTime 属性设置 ServicePointManager 在创建 ServicePoint 实例时分配给 MaxIdleTime 属性的最大空闲时间。对此值的更改将仅影响在值更改后初始化的 ServicePoint 实例。在 ServicePoint 空闲了 MaxIdleTime 中指定的时间后,它就有资格进行垃圾回收。当与 ServicePoint 关联的连接列表为空时,ServicePoint 处于空闲状态。
对我来说,所有这一切的关键是明确指出,在服务点空闲达到最大空闲时间后,它就有资格进行垃圾收集。我已经看到从 100 秒到 900 秒作为此值的默认值,具体取决于描述所涉及的框架版本。在我认为这是一个修复之前,我将做更多的测试。我很乐意从任何使用过这些属性的人那里解决他们的具体问题,以及作为我所看到的问题的根本原因,这是否有意义。
【问题讨论】:
-
RefreshDisplayDelegate发生了什么? -
RefreshDisplayDelegate 指向一个在发生超时时只调用控件上的 Refresh() 的方法。我在位图上画了一个字符串,表明相机丢失了通讯。我已将我从线程调用的两个委托添加到原始帖子中。感谢您的回复。
标签: c# windows-ce httprequest httpresponse compact-framework