简体   繁体   English

使用HttpClient异步方法C#时发生内存泄漏

[英]Memory leaks when using HttpClient Async methods c#

The following code is called many times per second. 以下代码每秒被调用多次。 If _client.PostJsonAsync throws an OperationCanceledException (eg due to a timeout) then Task.FromResult(default(T)) is returned however in the event that this happens memory usage (and CPU usage) rapidly starts to climb until OutOfMemoryExceptions are thrown (even when the exception is not thrown the memory always seems to climb albeit much slower). 如果_client.PostJsonAsync抛出OperationCanceledException(例如,由于超时),则返回Task.FromResult(default(T)),但是在这种情况下,内存使用率(和CPU使用率)迅速开始攀升,直到抛出OutOfMemoryExceptions(甚至当未引发异常时,内存似乎总是在爬升,尽管速度要慢得多。 Is there any problem with the code (or its over-use) that may be causing memory leaks? 代码是否存在任何问题(或其过度使用),可能导致内存泄漏?

    private async Task<T> GetResponse<T>(Dictionary<string, object> args, string method)
    {
        try
        {
            var response = await _client.PostAsJsonAsync(method, args);


            return await Deserialise<T>(response.Content); 

        }
        catch (OperationCanceledException)
        {
            return await Task.FromResult(default(T));
        }
    }

    private async Task<T> Deserialise<T>(HttpContent content)
    {
        using (var stream = await content.ReadAsStreamAsync())
        using (var streamReader = new StreamReader(stream))
        using (var reader = new JsonTextReader(streamReader))
        {
            var serializer = new JsonSerializer();
            try
            {
                return serializer.Deserialize<T>(reader);
            }
            catch (JsonSerializationException e)
            {
                throw new ClientException("Failed to deserialize object, see inner exception and content for more details", e) { Content = reader.ReadAsString() };
            }
        }
    }

I can point two things that makes your memory usage climb: 我可以指出两点使您的内存使用量增加:

  • As Nkosi pointed in a comment PostAsJsonAsync return type is HttpResponseMessage which implements IDisposable so you should dispose it, just add a using statement around it. 正如Nkosi在评论中指出的那样, PostAsJsonAsync返回类型是HttpResponseMessage ,它实现了IDisposable因此您应该对其进行处理,只需在其周围添加using语句即可。
    This is where your code leaks and the reason why your memory usage climbs even when no exceptions are thrown. 这是您的代码泄漏的地方,也是即使没有引发异常时内存使用量也会增加的原因。

  • The reason it's seems your memory usage climbs faster when exceptions are thrown is that besides your first problem in this scenario you create a lot of new tasks using Task.FromResult that adds pressure to your memory usage. 引发异常时,似乎您的内存使用量增长更快的原因是,除了这种情况下的第一个问题之外,您还使用Task.FromResult创建了许多新任务,这给您的内存使用量增加了压力。
    Though it does not seem to leak it will still make the GC work harder, this is why I suggest you to cache the task you return (you can reuse the same task instance because you always return the same value) or even better does not create new tasks at all. 尽管它似乎没有泄漏,但仍会使GC更加努力工作,这就是为什么我建议您缓存您返回的任务(您可以重复使用同一任务实例,因为您总是返回相同的值),甚至更好根本没有新任务。
    Since your method is already marked as async you can return default(T) right away and the compiler will generate the state machine for you and will wrap it inside a task (it will do it anyway, no reason to create and await a new task) . 由于您的方法已被标记为async您可以立即返回default(T) ,并且编译器将为您生成状态机并将其包装在任务中(无论如何,它都会这样做,没有理由创建并等待新任务)。

Your modified code should be look something like this: 您修改后的代码应如下所示:

private async Task<T> GetResponse<T>(Dictionary<string, object> args, string method)
{
    try
    {
        using(var response = await _client.PostAsJsonAsync(method, args))
        {
            return await Deserialise<T>(response.Content); 
        }
    }
    catch (OperationCanceledException)
    {
        return default(T);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM