Я перепробовал все возможности, чтобы избавиться от того, что я считаю утечкой памяти, с помощью классов 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» и call dispose для всех ojects, которые позволяют это. Кроме того, я уверен, чтобы прервать запрос, когда закончите. Я видел это в других примерах, и он должен освобождать ресурсы подключения и т. Д.
Вот странная часть, утечка происходит только при возникновении тайм-аута WebException, если у меня нет подключенных камер. При подключенных камерах устройства работают в течение нескольких дней без увеличения памяти. Кроме того, как количество управляемых байтов, так и общее количество байтов растет в оборотах в минуту, поэтому я не думаю, что это неуправляемая утечка. Наконец, я пытаюсь получить изображения с камеры так быстро, как могу. Я начинаю задумываться, не даю ли я время сборщикам сборщика мусора. Но когда происходит сбор (я вижу, что в 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 находится только в области действия во время запроса, я не вижу, как нельзя собрать какие-либо ссылочные ресурсы.
Update2:
Ну, я позволил ему работать с включенными камерами и позволил тайм-аутам продолжаться, затем я отключил камеры, исключив попытки HttpRequest и таймауты, и позволил ему работать до конца дня. В конце концов, GC застрял на том же значении (исходя из прошлого теста, он должен был возрасти примерно на 6 МБ), доказав, что это не имеет ничего общего с предоставлением GC времени для сбора, по крайней мере, я думаю. Таким образом, ресурсы все еще в подвешенном состоянии, и мне нужно выяснить, что именно держит их укоренившимися. Надеюсь, я могу понять, что
и дать еще одно обновление. До тех пор ...
Примечание стороны:
Кто-нибудь когда-либо использовал HttpRequest / HttpResponse для получения изображений с IP-камеры на устройстве WIN CE с использованием CF.NET 3.5?Если да, был ли тестовый случай потери связи с камеры на неопределенное время?Это должно было быть первым, что я спросил, так как я не нашел много примеров, показывающих, как общаться с IP-камерами со встроенных устройств.
Update3:
Ну, я думаю, что у меня естьнаткнулся на исправление для моей конкретной проблемы.Я внес несколько изменений в члены статического класса 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 секунд в качестве значения по умолчанию для этого значения, в зависимости от версии платформы, к которой относится описание.Я собираюсь сделать еще несколько испытаний, прежде чем я считаю, что это исправить.Мне бы очень хотелось, чтобы кто-нибудь, кто играл с этими свойствами, решал их конкретные проблемы и имеет ли это смысл в качестве основной причины проблемы, с которой я столкнулся.