【问题标题】:HttpRequest/HttpResponse Memory Leak? CF.NET 3.5 WIN CE 6.0HttpRequest/HttpResponse 内存泄漏? CF.NET 3.5 WIN CE 6.0
【发布时间】: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


【解决方案1】:

只需将HttpWebRequest 对象的AllowWriteStreamBuffering 属性设置为false:

HttpReq.AllowWriteStreamBuffering = false;
HttpReq.AllowAutoRedirect = false;

【讨论】:

  • 在compact框架中,AllowWriteStreamBuffering默认为false,但AllowAutoRedirect不是。我会试一试。我还注意到 KeepAlive 标志默认为 true。有一次,我将 AllowWriteStreamBuffering 和 KeepAlive 都明确设置为 false,并且没有区别。我会将它们全部默认为 false 并查看它的执行情况。感谢您的回复。
  • 是的,这并没有什么不同。还是谢谢。
【解决方案2】:

请参考帖子中的Update3。由于列出的原因,这似乎解决了我的问题。感谢您的回复。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-08
    • 1970-01-01
    • 1970-01-01
    • 2012-04-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多