作者:dashan | 来源:互联网 | 2023-05-28 18:30
我有10到150个长生命类对象,可以调用使用HttpClient执行简单HTTPS API调用的方法.PUT调用示例:
using (HttpClientHandler handler = new HttpClientHandler())
{
handler.UseCOOKIEs = true;
handler.COOKIECOntainer= _COOKIEs;
using (HttpClient client = new HttpClient(handler, true))
{
client.Timeout = new TimeSpan(0, 0, (int)(SettingsData.Values.ProxyTimeout * 1.5));
client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", Statics.UserAgent);
try
{
using (StringContent sData = new StringContent(data, Encoding.UTF8, contentType))
using (HttpResponseMessage respOnse= await client.PutAsync(url, sData))
{
using (var cOntent= response.Content)
{
ret = await content.ReadAsStringAsync();
}
}
}
catch (ThreadAbortException)
{
throw;
}
catch (Exception ex)
{
LastErrorText = ex.Message;
}
}
}
运行这些方法2-3个小时后,包括通过using
语句正确处理,程序已经爬到1GB-1.5GB的内存,并最终因各种内存不足错误而崩溃.很多时候连接是通过不可靠的代理,因此连接可能无法按预期完成(超时和其他错误很常见).
.NET Memory Profiler已经指出这HttpClientHandler
是主要的问题,声明它既有"具有直接委托根的Disposed实例"(红色感叹号)和"已经处置但仍未进行GC处理的实例"(黄色感叹号).探查器指示已经根植的代表是AsyncCallback
s,源自HttpWebRequest.
它还可能RemoteCertValidationCallback
与HTTPS证书验证有关,因为它TlsStream
是根目录中的"Disposed但not not GCed".
考虑到这一切 - 我怎样才能更正确地使用HttpClient并避免这些内存问题?我应该GC.Collect()
每小时强迫一次吗?我知道这被认为是不好的做法,但我不知道如何回收这个不太适当处理的内存,并且这些短命对象的更好的使用模式对我来说并不明显,因为它似乎是.NET对象本身的一个缺陷.
更新
强制GC.Collect()
没有效果.
进程的总管理字节数最多保持在20-30 MB左右,而进程总内存(在任务管理器中)继续爬升,表明存在非托管内存泄漏.因此,此使用模式正在创建非托管内存泄漏.
我已经尝试根据建议创建HttpClient和HttpClientHandler的类级别实例,但这没有明显的效果.即使我将这些设置为类级别,它们仍然会重新创建并且很少重复使用,因为代理设置通常需要更改.一旦请求启动,HttpClientHandler就不允许修改代理设置或任何属性,因此我不断重新创建处理程序,就像最初使用独立using
语句一样.
HttpClienthandler仍然使用"直接委托根"来处理AsyncCallback - > HttpWebRequest.我开始想知道HttpClient是否可能不是为快速请求和短生命对象而设计的.没有尽头......希望有人建议使用HttpClientHandler可行.
记忆探测器镜头:
1> 小智..:
使用repr形式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();
}
}
}
感谢您深究这一点.不幸的是,HttpClient类不符合我的要求 - 由于公共代理的动态和不稳定性,通常需要重新创建对象.看来HttpClient对于短生命连接来说不是一个可行的解决方案 - 更改代理设置需要重新构建HttpClientHandler,从而重新构建HttpClient.无论哪种方式,物体应该能够在不泄漏的情况下根据需要长寿或短缺; 这肯定是HttpClient中的一个缺陷.