c# - HttpClientHandler/HttpClient 内存泄漏

标签 c# memory memory-leaks garbage-collection httpclient

我有 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 是这里的主要问题,指出它同时具有“具有直接委托(delegate)根的已处置实例”(红色感叹号)和“已处置但尚未仍然没有 GCed'(黄色感叹号)。探查器指示已被 Root 的委托(delegate)是 AsyncCallback,源自 HttpWebRequest。

它也可能与 RemoteCertValidationCallback 相关,这与 HTTPS 证书验证有关,因为 TlsStream 是根中更下方的一个对象,它是“Disposed but not GCed” '。

考虑到所有这些 - 我怎样才能更正确地使用 HttpClient 并避免这些内存问题?我应该每隔一小时左右强制执行一次 GC.Collect() 吗?我知道这被认为是不好的做法,但我不知道如何回收这个没有完全正确处理的内存,并且这些短期对象的更好使用模式对我来说并不明显,因为它似乎是 .NET 对象本身的缺陷。


更新 强制 GC.Collect() 没有效果。

进程的总托管字节数最多保持在 20-30 MB 左右,而进程总内存(在任务管理器中)继续攀升,表明存在非托管内存泄漏。因此,这种使用模式会造成非托管内存泄漏。

我已尝试根据建议创建 HttpClient 和 HttpClientHandler 的类级别实例,但这没有明显的效果。即使我将这些设置为类级别,由于代理设置经常需要更改这一事实,它们仍然会重新创建并且很少重复使用。一旦发起请求,HttpClientHandler 不允许修改代理设置或任何属性,所以我不断地重新创建处理程序,就像最初使用独立的 using 语句所做的那样。

HttpClienthandler 仍然使用“直接委托(delegate)根”处理 AsyncCallback -> HttpWebRequest。我开始怀疑 HttpClient 是否不是为快速请求和短期对象而设计的。看不到尽头..希望有人建议使用 HttpClientHandler 可行。


内存分析器截图: Initial stack indicating that HttpClientHandler is the root issue, having 304 live instances that should have been GC'd

enter image description here

enter image description here

最佳答案

使用复制形式 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();
        }
    }
}

关于c# - HttpClientHandler/HttpClient 内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27732546/

相关文章:

c# - Sonar-msbuild-runner 退出并出现 255 错误,故障 CLR 模块

c# - Entity Framework Core 3,无需连接即可获取外键

c - 内存泄漏检测导致错误

c++ - OS API 在结构中分配成员。先释放结构还是先释放每个成员?

iphone - 如何处理核心数据保留周期

database - QSqlDatabase在不同线程中的并发查询

c - 函数内的 malloc char*,valgrind 报告内存泄漏

c# - 刷新高速缓存时如何提供过时的数据?

C# 用户输入设置数组大小和循环数组显示元素

php - 如何在linux中监控php的内存使用情况?