【问题标题】:UWP C# Windows.Web.HttpClient memory leakUWP C# Windows.Web.HttpClient 内存泄漏
【发布时间】:2021-04-09 10:07:45
【问题描述】:

我需要定期向网站发送 GET 请求。

protected override async void OnNavigatedTo(NavigationEventArgs e)
{
   base.OnNavigatedTo(e);

   HttpClient httpClient = new HttpClient();
   Uri requestUri = new Uri("https://pastebin.com/raw/1j9EAVUW");

   while (true)
   {
      await httpClient.GetAsync(requestUri);
      await Task.Delay(1000);
   }
}

但是,运行此代码会导致内存泄漏,并且 RAM 使用量每秒都会增加。

应用 using 无济于事,在循环内初始化 HttpClient 也无济于事。

我想出的唯一解决方案是手动调用GC.Collect() 每次循环执行,或每~10 次执行。但我读过这被认为是一种不好的做法。

还有其他方法吗?

HttpClient 文档:https://docs.microsoft.com/en-us/uwp/api/windows.web.http.httpclient?view=winrt-19041

【问题讨论】:

  • 如果GC.Collect()回收内存,那么显然没有内存泄漏。您选择了非确定性垃圾收集方案,这就是您所得到的。如果您需要一个具有可预测性能特征的系统,您将不得不使用一种支持确定性垃圾收集的编程语言,例如 C++。
  • 你是对的。我误用了“内存泄漏”一词。这只是 C# 的本质。我已经用 C++ 重写了代码,它工作得非常好,因为响应对象在超出范围时会自动销毁(一个循环执行)

标签: c# .net windows uwp memory-leaks


【解决方案1】:

当您在using 语句中使用HttpClient 实例时,一旦执行退出 using 语句的范围,HttpClient.Dispose() 将被自动调用以释放系统资源。高 RAM 消耗是因为垃圾收集没有及时收集释放的资源。但是垃圾回收会在需要时收集释放的资源,例如分配其他对象或内存条件低。您可以通过打开几个带有解决方案的 Visual Studio 来测试它,垃圾回收会收集从 HttpClient 实例释放的资源。

如果你想让垃圾收集器积极收集释放的资源,你可以将GCCollectionMode设置为优化,它指示垃圾收集器只有在确定收集将是有足够的生产力证明是合理的。

您可以参考document获得更多关于提高垃圾回收性能的信息。

【讨论】:

  • 谢谢!这就是我一直在寻找的答案。
【解决方案2】:

我不认为这是内存泄漏。正如 cmets 中所指出的,调用 GC.Collect() 正在释放内存这一事实意味着这里没有问题。垃圾收集器在 .Net 中的工作方式太长/太复杂,无法在这里解释,但要记住的是,收集垃圾并不是一项廉价的操作。事实上,GC 可能不得不在收集时暂停线程,因此 .Net 会在收集之前尝试等待,即便如此,这也取决于系统当前的内存压力。

另外需要注意的是,调用 ``GetAsync(Uri)``` 实际上会在返回之前下载整个 HTTP 响应,这意味着它可能正在分配内存来保存 http 响应。这是 GetASync 文档中的备注部分:

This operation will not block. The returned IAsyncOperationWithProgress (of HttpResponseMessage and HttpProgress) completes after the **whole response (including content) is read.**

如果您不需要内容,您可以通过告诉 HttpClient 在读取响应标头后返回来减少分配(因此它不会为响应的正文分配内存),您还可以处理响应对象释放所有使用的资源:

var response = await httpClient.GetAsync("http://example.com", HttpCompletionOption.ResponseHeadersRead);
response.Dispose();

不用担心添加 GC.Collect()... 将其留给 .Net/GC 来确定何时需要。

【讨论】:

  • 感谢您的回答。对于我的项目,我需要阅读(然后解析)响应内容。我省略了这一点,以提高问题的清晰度并将重点放在主要问题上。你建议我不要担心添加 GC.Collect()。然而,在执行几分钟后,保持不变会导致 RAM 消耗高得离谱。调用 .Dispose() 可以稍微减慢这个过程,但它不会阻止这种情况的发生。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-14
  • 1970-01-01
  • 1970-01-01
  • 2016-01-27
  • 2010-11-11
相关资源
最近更新 更多