我有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内存分析器显示HttpClientHandler
是主要问题,表示它具有“带有直接委托根”的已释放实例(红色感叹号)和“已释放但仍未GCed”的实例(黄色感叹号)。分析器指示的委托来自于HttpWebRequest,并且是AsyncCallback
。
这也可能与RemoteCertValidationCallback
相关,涉及HTTPS证书验证,因为位于该根目录下进一步的对象是“已释放但未GCed”的TlsStream
。
考虑到所有这些 - 我怎样才能更正确地使用HttpClient并避免这些内存问题?我应该每隔一个小时强制使用GC.Collect()
吗?我知道那被认为是不好的做法,但我不知道如何回收这些没有被正确处理的内存,对于这些短暂对象的更好使用模式对我来说看不出来,因为它似乎是.NET对象本身的一个缺陷。
更新
强制使用GC.Collect()
没有效果。
进程的总托管字节数始终保持在最多20-30 MB左右,而进程的整体内存(在任务管理器中)继续上升,表明存在未受控制的内存泄漏。因此,这种使用模式会创建未受控制的内存泄漏。
我尝试了按建议创建HttpClient和HttpClientHandler类级别的实例,但这没有什么明显的效果。即使将它们设置为类级别,由于代理设置经常需要更改,它们仍然会被重新创建并很少被重复使用。HttpClientHandler不允许修改代理设置或任何属性,一旦发起请求就无法进行修改,因此我不断地重新创建处理程序,就像最初独立使用using语句时所做的那样。
HttpClienthandler仍然被释放,并带有到AsyncCallback -> HttpWebRequest的“直接委托根”。我开始怀疑HttpClient只是没设计用于快速请求和短暂生存的对象。看不到终点..希望有人能提出建议,使HttpClientHandler的使用变得可行。
内存分析器截图:
handler.CookieContainer = _CookieContainer
- 也许有一些可疑的事情正在进行? - Erti-Chris Eelmaa