Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/268.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/apache-flex/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# HttpClientHandler/HttpClient内存泄漏_C#_Memory_Memory Leaks_Garbage Collection_Httpclient - Fatal编程技术网

C# HttpClientHandler/HttpClient内存泄漏

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

我有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,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作为短期对象使用:


不确定这个包的来源是什么,但是,只要我引用它,内存泄漏就消失了。确保删除对内置.NET
System.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;
//你需要