C# HttpClientHandler/HttpClient内存泄漏
我有10-150个长寿类对象,它们调用使用HttpClient执行简单HTTPS API调用的方法。PUT调用的示例:C# HttpClientHandler/HttpClient内存泄漏,c#,memory,memory-leaks,garbage-collection,httpclient,C#,Memory,Memory Leaks,Garbage Collection,Httpclient,我有10-150个长寿类对象,它们调用使用HttpClient执行简单HTTPS API调用的方法。PUT调用的示例: 使用(HttpClientHandler handler=new-HttpClientHandler()) { handler.UseCookies=true; handler.CookieContainer=\u Cookies; 使用(HttpClient=newhttpclient(handler,true)) { client.Timeout=newtimespan(0
使用(HttpClientHandler handler=new-HttpClientHandler())
{
handler.UseCookies=true;
handler.CookieContainer=\u Cookies;
使用(HttpClient=newhttpclient(handler,true))
{
client.Timeout=newtimespan(0,0,(int)(setingsdata.Values.ProxyTimeout*1.5));
client.DefaultRequestHeaders.TryAddWithoutValidation(“用户代理”,Statics.UserAgent);
尝试
{
使用(StringContent sData=新的StringContent(数据,Encoding.UTF8,contentType))
使用(httpresponsemessageresponse=wait client.PutAsync(url,sData))
{
使用(var content=response.content)
{
ret=wait content.ReadAsStringAsync();
}
}
}
捕获(线程异常)
{
投掷;
}
捕获(例外情况除外)
{
LastErrorText=ex.消息;
}
}
}
在运行这些方法(包括通过使用
语句进行适当处理)2-3小时后,程序的内存已逐渐增加到1GB-1.5GB,并最终因各种内存不足错误而崩溃。很多时候,连接是通过不可靠的代理进行的,因此连接可能无法按预期完成(超时和其他错误很常见)
.NET内存探查器指出,HttpClientHandler
是这里的主要问题,指出它既有“具有直接委托根的已处置实例”(红色感叹号),也有“已处置但仍未GCed的实例”(黄色感叹号)。探查器指示已根化的委托是源自HttpWebRequest的AsyncCallback
s
它还可能与RemoteCertValidationCallback
有关,这与HTTPS证书验证有关,因为TlsStream
是根目录下的一个对象,它是“已处置但未GCed”
考虑到所有这些,我如何才能更正确地使用HttpClient并避免这些内存问题?我应该每隔一小时左右强制执行一次GC.Collect()
?我知道这被认为是一种不好的做法,但我不知道如何回收这些未被正确处理的内存,而且对于这些短期对象更好的使用模式对我来说并不明显,因为这似乎是.NET对象本身的一个缺陷
更新 强制执行
GC.Collect()
没有效果
进程的总托管字节数最多保持在20-30 MB左右,而进程的总体内存(在任务管理器中)继续攀升,这表明存在非托管内存泄漏。因此,这种使用模式正在创建非托管内存泄漏
根据建议,我尝试创建了HttpClient和HttpClientHandler的类级实例,但没有明显的效果。即使我将它们设置为类级别,它们仍然会被重新创建,很少被重新使用,因为代理设置经常需要更改。HttpClientHandler不允许在启动请求后修改代理设置或任何属性,因此我不断地重新创建处理程序,就像最初使用语句对独立的所做的那样
HttpClienthandler仍被处理为AsyncCallback->HttpWebRequest的“直接委托根”。我开始怀疑HttpClient是否不是为快速请求和短生命对象而设计的。看不到尽头。。希望有人能提出建议,使HttpClientHandler的使用变得可行
内存分析器快照:
使用Alexandr Nikitin的复制表单,我发现只有当您将HttpClient作为一个短期对象时,才会发生这种情况。如果使处理程序和客户机长寿,则似乎不会发生这种情况:
using System;
using System.Net.Http;
using System.Threading.Tasks;
namespace HttpClientMemoryLeak
{
using System.Net;
using System.Threading;
class Program
{
static HttpClientHandler handler = new HttpClientHandler();
private static HttpClient client = new HttpClient(handler);
public static async Task TestMethod()
{
try
{
using (var response = await client.PutAsync("http://localhost/any/url", null))
{
}
}
catch
{
}
}
static void Main(string[] args)
{
for (int i = 0; i < 1000000; i++)
{
Thread.Sleep(10);
TestMethod();
}
Console.WriteLine("Finished!");
Console.ReadKey();
}
}
}
使用系统;
使用System.Net.Http;
使用System.Threading.Tasks;
命名空间HttpClientMemoryLeak
{
Net系统;
使用系统线程;
班级计划
{
静态HttpClientHandler处理程序=新的HttpClientHandler();
私有静态HttpClient客户端=新HttpClient(处理程序);
公共静态异步任务TestMethod()
{
尝试
{
使用(var response=await client.PutAsync(“http://localhost/any/url“,空”)
{
}
}
抓住
{
}
}
静态void Main(字符串[]参数)
{
对于(int i=0;i<1000000;i++)
{
睡眠(10);
TestMethod();
}
控制台。WriteLine(“完成!”);
Console.ReadKey();
}
}
}
正如Matt Clark所提到的,默认的HttpClient
在您将其用作短期对象并根据请求创建新的HttpClient时会泄漏
作为一种解决方法,我能够通过使用以下Nuget包而不是内置的System.Net.Http
程序集,继续将HttpClient作为短期对象使用:
不确定这个包的来源是什么,但是,只要我引用它,内存泄漏就消失了。确保删除对内置.NETSystem.NET.Http
库的引用,并改用Nuget包 这就是我在不重新创建对象的情况下更改HttpClientHandler
代理的方式
public static void ChangeProxy(this HttpClientHandler handler, WebProxy newProxy)
{
if (handler.Proxy is WebProxy currentHandlerProxy)
{
currentHandlerProxy.Address = newProxy.Address;
currentHandlerProxy.Credentials = newProxy.Credentials;
}
else
{
handler.Proxy = newProxy;
}
}
下面是一个基本的Api客户端,它有效地使用了HttpClient和HttpClientHandler。不要为每个请求重新创建HTTPClient。尽可能重用Httpclient
我的性能Api客户端
使用系统;
Net系统;
使用System.Net.Http;
使用System.Net.Http.Header;
使用系统文本;
使用System.Threading.Tasks;
//你需要