问题描述
我在使用 httpClient.GetAsync
时遇到内存泄漏问题。
在任务管理器中,进程保持高内存消耗,不会被释放。
这是我的代码:
HttpClient httpClient = new HttpClient();
httpClient.Timeout = TimeSpan.FromMinutes(2);
try
{
using (var response = await httpClient.GetAsync(fullURL))
{
if (!response.IsSuccessstatusCode)
{
Logger.Default.Error($"error code {(int)response.StatusCode} - {response.StatusCode}");
return null;
}
using (MemoryStream memStream = new MemoryStream())
{
await response.Content.copyToAsync(memStream);
Logger.Default.Debug($"finished reading response,sized {Math.Round(memStream.Length / Math.Pow(1024,2),2)} MB");
}
}
}
catch (TaskCanceledException ex)
{
Logger.Default.Error($"Request timed out. {ex.Message}\n{ex.StackTrace}");
return null;
}
但是,当我改用 httpClient.GetStreamAsync
并更改时:
await response.copyToAsync(memStream);
致:
await response.Content.copyToAsync(memStream);
内存确实在几秒钟后被释放。
但我更愿意使用 GetAsync
,因为它为我提供了有关状态代码的信息,而 GetStreamAsync
没有。
我已经尝试在对象被释放后调用垃圾收集器 (GC.Collect(2)
),但没有帮助。
我做错了什么?
解决方法
HTTPClient 之前给我带来了这样的问题。我发现这篇文章很有帮助:
https://www.aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/
基本上,即使它实现了 IDisposable,您也可以在应用程序的整个生命周期内将它实例化为单例。这允许应用仅使用该 HTTPClient 连接,而不会导致连接未正确处理的问题。
如果您在 using 块中使用它,Windows 将在此状态下保持连接 240 秒。它由以下设置:
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpTimedWaitDelay]
试试这个:
using System;
using System.Net.Http;
namespace ConsoleApplication
{
public class Program
{
private static HttpClient Client = new HttpClient();
public static async Task Main(string[] args)
{
Console.WriteLine("Starting connections");
for(int i = 0; i<10; i++)
{
var result = await Client.GetAsync("http://aspnetmonsters.com");
Console.WriteLine(result.StatusCode);
}
Console.WriteLine("Connections done");
Console.ReadLine();
}
}
}
如果通过 using 语句语法实现 IDisposable,连接实际上在 using 块结束后保持打开状态。然后,当您的应用程序遇到另一个 using 块 HTTPClient 时,先前存在的一次尚未关闭。这可能会导致各种错误,最常见的是:
Windows 打开新套接字的速度是有限制的,所以如果你耗尽了连接池,那么你很可能会看到如下错误:
Unable to connect to the remote server
System.Net.Sockets.SocketException: Only one usage of each socket address (protocol/network address/port) is normally permitted.
互联网上的其他一些人找到了更好的方法来做到这一点,我个人没有使用它们的经验,但这里有一些值得一试的链接。这些描述了在不建立 HTTPClient 单例的情况下使用 IHTTPClientFactory 的方法和其他方法: