Утечка памяти HttpRequest / HttpResponse? CF.NET 3.5 WIN CE 6.0

RPM Heap Compare

Я испробовал все возможное, чтобы избавиться от того, что я считаю утечкой памяти, с помощью классов HttpRequest или HttpResponse в CF.NET 3.5, работающих на устройстве Win CE 6.0. Я использую их для связи с 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 () и т. д., которые я использовал для рисования Bitmap на экране для камеры:

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 и таймауты, и позволил ему работать до конца дня. В конце концов, сборщик мусора застрял на том же значении (по результатам прошлого теста он должен был увеличиться примерно на 6 МБ), доказывая, что это не имеет ничего общего с предоставлением сборщику мусора времени на сбор данных, по крайней мере, я думаю. Таким образом, ресурсы по-прежнему находятся в подвешенном состоянии, и мне нужно точно выяснить, что их поддерживает. Надеюсь, я смогу понять, что и дать еще одно обновление. А пока ...

Боковое примечание:

Кто-нибудь когда-нибудь использовал HttpRequest / HttpResponse для получения изображений с IP-камеры на устройстве WIN CE с использованием CF.NET 3.5? Если да, то был ли контрольный пример потери связи с камерой на неопределенное время? Это должно было быть первое, что я спросил, так как я не нашел там много примеров, показывающих, как взаимодействовать с IP-камерами со встроенных устройств.

Обновление 3:

Что ж, я думаю, что наткнулся на исправление для своего конкретная проблема. Я внес несколько изменений в члены статического класса ServicePointManager в отношении количества подключений по умолчанию и максимального времени простоя:

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

Поскольку у меня будет подключено максимум 4 камеры в любое время, и поскольку мой тайм-аут для HttpRequest равен установлен на 5000 мс, я решил попробовать максимальное время простоя 1000 мс, чтобы посмотреть, что произойдет. Я позволил двум устройствам работать на ночь без подключенных камер (тайм-аут каждые 5000 мс). Обычно происходит следующее: я приходил утром, и устройства сидели там с сообщением OOM, а память GC и физическая память для моей системы были исчерпаны. Что ж, у обоих устройств был тот же уровень памяти, на котором они были, когда я уходил прошлой ночью. Итак, я надеюсь, что это решение моей проблемы. На основе документации MSDN:

Свойство ConnectionLimit устанавливает максимальное количество подключений, которое ServicePoint может установить к Интернет-ресурсу. Значение свойства ConnectionLimit устанавливается равным значению свойства ServicePointManager.DefaultConnectionLimit при создании ServicePoint; последующие изменения в DefaultConnectionLimit не влияют на существующие экземпляры ServicePoint.

Свойство MaxIdleTime содержит время в миллисекундах, в течение которого ServicePoint может поддерживать простаивающее соединение с Интернет-ресурсом, прежде чем оно будет повторно использовано для использования в другом соединении . Вы можете установить для MaxIdleTime значение Timeout.Infinite, чтобы указать, что ServicePoint никогда не должен отключаться по таймауту. Значение свойства MaxIdleTime по умолчанию - это значение свойства ServicePointManager.MaxServicePointIdleTime при создании ServicePoint. Последующие изменения свойства MaxServicePointIdleTime не влияют на существующие экземпляры ServicePoint.

Свойство MaxServicePointIdleTime устанавливает максимальное время простоя, которое ServicePointManager назначает свойству MaxIdleTime при создании экземпляров ServicePoint. Изменения этого значения повлияют только на экземпляры ServicePoint, которые инициализируются после изменения значения. После простоя ServicePoint в течение времени, указанного в MaxIdleTime, он имеет право на сборку мусора. ServicePoint бездействует, когда список подключений, связанных с ServicePoint, пуст.

Ключ ко всему этому для меня состоит в том, что конкретно указывается, что после того, как точка обслуживания простаивает в течение максимального времени простоя, она имеет право на сборку мусора. . Я видел от 100 до 900 секунд по умолчанию для этого значения, в зависимости от версии фреймворка, к которой относилось описание. Я собираюсь провести еще несколько тестов, прежде чем сочту это исправлением.Я хотел бы получить здесь ответ от всех, кто играл с этими свойствами, чтобы исправить свои конкретные проблемы и выяснить, имеет ли это смысл как основную причину проблемы, которую я видел.

9
задан Azat Ibrakov 15 March 2019 в 10:27
поделиться